pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
ExecRTCheckPerms() and many prunable partitions
97+ messages / 11 participants
[nested] [flat]

Commits from this thread:
  2023-02-20 a316a3bc6d  Correctly set userid of subquery relations' child rels
  2023-02-13 c7468c73f7  Fix buggy recursion in flatten_rtes_walker().
  2023-01-19 438e6b7240  Remove some dead code in selfuncs.c
  2022-12-07 29861e228a  Update outdated comment in ApplyRetrieveRule
  2022-12-06 a61b1f7482  Rework query relation permission checking
  2022-12-02 fb958b5da8  Generalize ri_RootToPartitionMap to use for non-partition children
  2022-11-30 599b33b949  Stop accessing checkAsUser via RTE in some cases
  2022-11-29 ad86d159b6  Add 'missing_ok' argument to build_attrmap_by_name
* ExecRTCheckPerms() and many prunable partitions
@ 2021-06-30 13:33  Amit Langote <amitlangote09@gmail.com>
  0 siblings, 2 replies; 97+ messages in thread

From: Amit Langote @ 2021-06-30 13:33 UTC (permalink / raw)
  To: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,

Last year in [1], I had briefly mentioned $subject.  I'm starting this
thread to propose a small patch to alleviate the inefficiency of that
case.

As also mentioned in [1], when running -Mprepared benchmarks
(plan_cache_mode = force_generic_plan) using partitioned tables,
ExecRTCheckPerms() tends to show up in the profile, especially with
large partition counts.  Granted it's lurking behind
AcquireExecutorLocks(), LockReleaseAll() et al, but still seems like a
problem we should do something about.

The problem is that it loops over the entire range table even though
only one or handful of those entries actually need their permissions
checked.  Most entries, especially those of partition child tables
have their requiredPerms set to 0, which David pointed out to me in
[2], so what ExecCheckRTPerms() does in their case is pure overhead.

An idea to fix that is to store the RT indexes of the entries that
have non-0 requiredPerms into a separate list or a bitmapset in
PlannedStmt.  I thought of two implementation ideas for how to set
that:

1. Put add_rtes_to_flat_rtable() in the charge of populating it:

@@ -324,12 +324,18 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
     * flattened rangetable match up with their original indexes.  When
     * recursing, we only care about extracting relation RTEs.
     */
+   rti = 1;
    foreach(lc, root->parse->rtable)
    {
        RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);

        if (!recursing || rte->rtekind == RTE_RELATION)
+       {
            add_rte_to_flat_rtable(glob, rte);
+           if (rte->requiredPerms != 0)
+               glob->checkPermRels = bms_add_member(glob->checkPermRels, rti);
+       }
+       rti++
    }

2. Start populating checkPermRels in ParseState (parse_relation.c),
passing it along in Query through the rewriter and finally the
planner.

1 seems very simple, but appears to add overhead to what is likely a
oft-taken path.  Also, the newly added code would have to run as many
times as there are partitions, which sounds like a dealbreaker to me.

2 can seem a bit complex.  Given that the set is tracked in Query,
special care is needed to handle views and subqueries correctly,
because those features involve intricate manipulation of Query nodes
and their range tables.  However, most of that special care code
remains out of the busy paths.  Also, none of that code touches
partition/child RTEs, so unaffected by how many of them there are.

For now, I have implemented the idea 2 as the attached patch.  While
it passes make check-world, I am not fully confident yet that it
correctly handles all the cases involving views and subqueries.

So while still kind of PoC, will add this to July CF for keeping track.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

[1] https://www.postgresql.org/message-id/CA+HiwqG7ZruBmmih3wPsBZ4s0H2EhywrnXEduckY5Hr3fWzPWA@mail.gmail...
[2] https://www.postgresql.org/message-id/CAApHDvqPzsMcKLRpmNpUW97PmaQDTmD7b2BayEPS5AN4LY-0bA%40mail.gma...

Attachments:

0001-Explicitly-track-RT-indexes-of-relations-to-check-pe.patch (application/octet-stream, 20.0K) download From 6d429165eda81f1c8e1dc1781e5b571788293dcc Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 30 Jun 2021 20:08:25 +0900 Subject: [PATCH] Explicitly track RT indexes of relations to check permissions of --- src/backend/commands/copy.c | 2 +- src/backend/executor/execMain.c | 14 ++++++++------ src/backend/executor/execParallel.c | 1 + src/backend/nodes/copyfuncs.c | 2 ++ src/backend/nodes/equalfuncs.c | 1 + src/backend/nodes/outfuncs.c | 2 ++ src/backend/nodes/readfuncs.c | 2 ++ src/backend/optimizer/plan/planner.c | 1 + src/backend/optimizer/plan/setrefs.c | 12 ++++++++++++ src/backend/optimizer/prep/prepjointree.c | 2 ++ src/backend/parser/analyze.c | 8 ++++++++ src/backend/parser/parse_relation.c | 11 +++++++++++ src/backend/parser/parse_utilcmd.c | 1 + src/backend/rewrite/rewriteHandler.c | 12 ++++++++++++ src/backend/rewrite/rewriteManip.c | 2 ++ src/backend/utils/adt/ri_triggers.c | 4 +++- src/include/executor/executor.h | 3 ++- src/include/nodes/parsenodes.h | 2 ++ src/include/nodes/plannodes.h | 3 +++ src/include/parser/parse_node.h | 2 ++ 20 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 8265b981eb..500dc51b15 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -158,7 +158,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, else rte->selectedCols = bms_add_member(rte->selectedCols, attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckRTPerms(pstate->p_rtable, bms_make_singleton(1), true); /* * Permission check for row security policies. diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b3ce4bae53..8ed6dd6235 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -553,7 +553,7 @@ ExecutorRewind(QueryDesc *queryDesc) /* * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * Check access permissions for the specified relations in a range table. * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,14 +565,16 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckRTPerms(List *rangeTable, Bitmapset *checkPermRels, + bool ereport_on_violation) { - ListCell *l; + int rti; bool result = true; - foreach(l, rangeTable) + rti = -1; + while ((rti = bms_next_member(checkPermRels, rti)) > 0) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RangeTblEntry *rte = (RangeTblEntry *) list_nth(rangeTable, rti - 1); result = ExecCheckRTEPerms(rte); if (!result) @@ -815,7 +817,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) /* * Do permissions checks */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckRTPerms(rangeTable, plannedstmt->checkPermRels, true); /* * initialize the node's execution state diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 12c41d746b..21971f1e10 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->checkPermRels = NULL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index bd87f23784..f9241f56ca 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -90,6 +90,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_BITMAPSET_FIELD(checkPermRels); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -3176,6 +3177,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_BITMAPSET_FIELD(checkPermRels); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index dba3e6b31e..0313b2a469 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -979,6 +979,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_BITMAPSET_FIELD(checkPermRels); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index e32b92e299..7bee62dc75 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -308,6 +308,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_BITMAPSET_FIELD(checkPermRels); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -3071,6 +3072,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_BITMAPSET_FIELD(checkPermRels); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index f0b34ecfac..f0f80b5ac4 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -266,6 +266,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_BITMAPSET_FIELD(checkPermRels); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1589,6 +1590,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_BITMAPSET_FIELD(checkPermRels); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 1868c4eff4..ce8484fe21 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -513,6 +513,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->checkPermRels = parse->checkPermRels; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 61ccfd300b..592de92aba 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -360,6 +360,8 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) if (rel != NULL) { + int rtoff = list_length(glob->finalrtable); + Assert(rel->relid == rti); /* sanity check on array */ /* @@ -387,6 +389,16 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) IS_DUMMY_REL(fetch_upper_rel(rel->subroot, UPPERREL_FINAL, NULL))) add_rtes_to_flat_rtable(rel->subroot, true); + + /* + * Pull up the checkPermRels set of subqueries that themselves + * were not. + */ + Assert(rte->subquery != NULL); + root->parse->checkPermRels = + bms_union(root->parse->checkPermRels, + offset_relid_set(rte->subquery->checkPermRels, + rtoff)); } } rti++; diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 62a1668796..ec057e1335 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1132,6 +1132,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + parse->checkPermRels = bms_union(parse->checkPermRels, subquery->checkPermRels); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 438b077004..d698191b0c 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -895,6 +896,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1348,6 +1350,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1585,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1832,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2286,6 +2291,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2352,6 +2358,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2711,6 +2718,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->checkPermRels= pstate->p_check_perm_rels; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 7465919044..7479e2c5a2 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1464,6 +1464,8 @@ addRangeTableEntry(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + pstate->p_check_perm_rels = bms_add_member(pstate->p_check_perm_rels, + list_length(pstate->p_rtable)); /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -1552,6 +1554,8 @@ addRangeTableEntryForRelation(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + pstate->p_check_perm_rels = bms_add_member(pstate->p_check_perm_rels, + list_length(pstate->p_rtable)); /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -1649,6 +1653,7 @@ addRangeTableEntryForSubquery(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + /* No need to add this RTE's index to pstate->p_check_perm_rels. */ /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -1955,6 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + /* No need to add this RTE's index to pstate->p_check_perm_rels. */ /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -2026,6 +2032,7 @@ addRangeTableEntryForTableFunc(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + /* No need to add this RTE's index to pstate->p_check_perm_rels. */ /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -2113,6 +2120,7 @@ addRangeTableEntryForValues(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + /* No need to add this RTE's index to pstate->p_check_perm_rels. */ /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -2204,6 +2212,7 @@ addRangeTableEntryForJoin(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + /* No need to add this RTE's index to pstate->p_check_perm_rels. */ /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -2354,6 +2363,7 @@ addRangeTableEntryForCTE(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + /* No need to add this RTE's index to pstate->p_check_perm_rels. */ /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace @@ -2477,6 +2487,7 @@ addRangeTableEntryForENR(ParseState *pstate, * appropriate. */ pstate->p_rtable = lappend(pstate->p_rtable, rte); + /* No need to add this RTE's index to pstate->p_check_perm_rels. */ /* * Build a ParseNamespaceItem, but don't add it to the pstate's namespace diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 81d3e7990c..65385d310a 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -3069,6 +3069,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->checkPermRels = pstate->p_check_perm_rels; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 88a9e95e33..1b393d5710 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -420,6 +420,8 @@ rewriteRuleAction(Query *parsetree, */ sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + sub_action->checkPermRels = bms_union(parsetree->checkPermRels, + sub_action->checkPermRels); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1720,6 +1722,9 @@ ApplyRetrieveRule(Query *parsetree, rte = rt_fetch(rt_index, parsetree->rtable); newrte = copyObject(rte); parsetree->rtable = lappend(parsetree->rtable, newrte); + parsetree->checkPermRels = + bms_add_member(parsetree->checkPermRels, + list_length(parsetree->rtable)); parsetree->resultRelation = list_length(parsetree->rtable); /* @@ -1842,6 +1847,11 @@ ApplyRetrieveRule(Query *parsetree, rte->updatedCols = NULL; rte->extraUpdatedCols = NULL; + /* Update checkPermRels set in the respective Query nodes. */ + parsetree->checkPermRels = bms_del_member(parsetree->checkPermRels, rt_index); + rule_action->checkPermRels = bms_add_member(rule_action->checkPermRels, + PRS2_OLD_VARNO); + return parsetree; } @@ -3195,6 +3205,8 @@ rewriteTargetView(Query *parsetree, Relation view) parsetree->rtable = lappend(parsetree->rtable, new_rte); new_rt_index = list_length(parsetree->rtable); + parsetree->checkPermRels = bms_add_member(parsetree->checkPermRels, + new_rt_index); /* * INSERTs never inherit. For UPDATE/DELETE, we use the view query's diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index d4e0b8b4de..0a0f5f2d5c 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -461,6 +461,8 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) rc->rti += offset; } + + qry->checkPermRels = offset_relid_set(qry->checkPermRels, offset); } query_tree_walker(qry, OffsetVarNodes_walker, (void *) &context, 0); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 96269fc2ad..83536afa23 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1352,7 +1352,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), + bms_add_member(bms_make_singleton(1), 2), + false)) return false; /* diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 3dc03c913e..0e679b66b5 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckRTPerms(List *rangeTable, Bitmapset *checkPermRels, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index def9651b34..a27fd30093 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + Bitmapset *checkPermRels; /* range table indexes of relations to check + * the permissions of */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index aaa3b65d04..1b90c38034 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -65,6 +65,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + Bitmapset *checkPermRels; /* range table indexes of relations to check + * the permissions of */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 1500de2dd0..6b93988eb9 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + Bitmapset *p_check_perm_rels; /* range table indexes of relations to + * check the permissions of */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-06-30 14:33  David Rowley <dgrowleyml@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  1 sibling, 1 reply; 97+ messages in thread

From: David Rowley @ 2021-06-30 14:33 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, 1 Jul 2021 at 01:34, Amit Langote <amitlangote09@gmail.com> wrote:
> For now, I have implemented the idea 2 as the attached patch.

I only just had a fleeting glance at the patch. Aren't you
accidentally missing the 0th RTE here?

+ while ((rti = bms_next_member(checkPermRels, rti)) > 0)
  {
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+ RangeTblEntry *rte = (RangeTblEntry *) list_nth(rangeTable, rti - 1);

I'd have expected >= 0 rather than > 0.

David





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-06-30 14:58  Amit Langote <amitlangote09@gmail.com>
  parent: David Rowley <dgrowleyml@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2021-06-30 14:58 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Jun 30, 2021 at 23:34 David Rowley <dgrowleyml@gmail.com> wrote:

> On Thu, 1 Jul 2021 at 01:34, Amit Langote <amitlangote09@gmail.com> wrote:
> > For now, I have implemented the idea 2 as the attached patch.
>
> I only just had a fleeting glance at the patch. Aren't you
> accidentally missing the 0th RTE here?
>
> + while ((rti = bms_next_member(checkPermRels, rti)) > 0)
>   {
> - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
> + RangeTblEntry *rte = (RangeTblEntry *) list_nth(rangeTable, rti - 1);
>
> I'd have expected >= 0 rather than > 0.


Hmm, a valid RT index cannot be 0, so that seems fine to me.  Note that RT
indexes are added as-is to that bitmapset, not after subtracting 1.

> --
Amit Langote
EDB: http://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-06-30 22:51  David Rowley <dgrowleyml@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 0 replies; 97+ messages in thread

From: David Rowley @ 2021-06-30 22:51 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, 1 Jul 2021 at 02:58, Amit Langote <amitlangote09@gmail.com> wrote:
>
> On Wed, Jun 30, 2021 at 23:34 David Rowley <dgrowleyml@gmail.com> wrote:
>> + while ((rti = bms_next_member(checkPermRels, rti)) > 0)
>>   {
>> - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
>> + RangeTblEntry *rte = (RangeTblEntry *) list_nth(rangeTable, rti - 1);
>>
>> I'd have expected >= 0 rather than > 0.
>
> Hmm, a valid RT index cannot be 0, so that seems fine to me.  Note that RT indexes are added as-is to that bitmapset, not after subtracting 1.

Oh, you're right. My mistake.

David





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-07-01 15:45  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Amit Langote <amitlangote09@gmail.com>
  1 sibling, 1 reply; 97+ messages in thread

From: Tom Lane @ 2021-07-01 15:45 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Amit Langote <amitlangote09@gmail.com> writes:
> The problem is that it loops over the entire range table even though
> only one or handful of those entries actually need their permissions
> checked.  Most entries, especially those of partition child tables
> have their requiredPerms set to 0, which David pointed out to me in
> [2], so what ExecCheckRTPerms() does in their case is pure overhead.

> An idea to fix that is to store the RT indexes of the entries that
> have non-0 requiredPerms into a separate list or a bitmapset in
> PlannedStmt.

I think perhaps we ought to be more ambitious than that, and consider
separating the list of permissions-to-check from the rtable entirely.
Your patch hardly qualifies as non-invasive, plus it seems to invite
errors of omission, while if we changed the data structure altogether
then the compiler would help find any not-updated code.

But the main reason that this strikes me as possibly a good idea
is that I was just taking another look at the complaint in [1],
where I wrote

>> I think it's impossible to avoid less-than-O(N^2) growth on this sort
>> of case.  For example, the v2 subquery initially has RTEs for v2 itself
>> plus v1.  When we flatten v1 into v2, v2 acquires the RTEs from v1,
>> namely v1 itself plus foo.  Similarly, once vK-1 is pulled up into vK,
>> there are going to be order-of-K entries in vK's rtable, and that stacking
>> makes for O(N^2) work overall just in manipulating the rtable.
>> 
>> We can't get rid of these rtable entries altogether, since all of them
>> represent table privilege checks that the executor will need to do.

Perhaps, if we separated the rtable from the required-permissions data
structure, then we could avoid pulling up otherwise-useless RTEs when
flattening a view (or even better, not make the extra RTEs in the
first place??), and thus possibly avoid that exponential planning-time
growth for nested views.

Or maybe not.  But I think we should take a hard look at whether
separating these data structures could solve both of these problems
at once.

			regards, tom lane

[1] https://www.postgresql.org/message-id/flat/797aff54-b49b-4914-9ff9-aa42564a4d7d%40www.fastmail.com





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-07-02 00:40  Amit Langote <amitlangote09@gmail.com>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 2 replies; 97+ messages in thread

From: Amit Langote @ 2021-07-02 00:40 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Jul 2, 2021 at 12:45 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Amit Langote <amitlangote09@gmail.com> writes:
> > The problem is that it loops over the entire range table even though
> > only one or handful of those entries actually need their permissions
> > checked.  Most entries, especially those of partition child tables
> > have their requiredPerms set to 0, which David pointed out to me in
> > [2], so what ExecCheckRTPerms() does in their case is pure overhead.
>
> > An idea to fix that is to store the RT indexes of the entries that
> > have non-0 requiredPerms into a separate list or a bitmapset in
> > PlannedStmt.
>
> I think perhaps we ought to be more ambitious than that, and consider
> separating the list of permissions-to-check from the rtable entirely.
> Your patch hardly qualifies as non-invasive, plus it seems to invite
> errors of omission, while if we changed the data structure altogether
> then the compiler would help find any not-updated code.
>
> But the main reason that this strikes me as possibly a good idea
> is that I was just taking another look at the complaint in [1],
> where I wrote
>
> >> I think it's impossible to avoid less-than-O(N^2) growth on this sort
> >> of case.  For example, the v2 subquery initially has RTEs for v2 itself
> >> plus v1.  When we flatten v1 into v2, v2 acquires the RTEs from v1,
> >> namely v1 itself plus foo.  Similarly, once vK-1 is pulled up into vK,
> >> there are going to be order-of-K entries in vK's rtable, and that stacking
> >> makes for O(N^2) work overall just in manipulating the rtable.
> >>
> >> We can't get rid of these rtable entries altogether, since all of them
> >> represent table privilege checks that the executor will need to do.
>
> Perhaps, if we separated the rtable from the required-permissions data
> structure, then we could avoid pulling up otherwise-useless RTEs when
> flattening a view (or even better, not make the extra RTEs in the
> first place??), and thus possibly avoid that exponential planning-time
> growth for nested views.
>
> Or maybe not.  But I think we should take a hard look at whether
> separating these data structures could solve both of these problems
> at once.

Ah, okay.  I'll think about decoupling the permission checking stuff
from the range table data structure.

Thanks for the feedback.

I'll mark the CF entry as WoA, unless you'd rather I just mark it RwF.

-- 
Amit Langote
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-07-07 04:41  David Rowley <dgrowleyml@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  1 sibling, 1 reply; 97+ messages in thread

From: David Rowley @ 2021-07-07 04:41 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, 2 Jul 2021 at 12:41, Amit Langote <amitlangote09@gmail.com> wrote:
> I'll mark the CF entry as WoA, unless you'd rather I just mark it RwF.

I've set it to waiting on author. It was still set to needs review.

If you think you'll not get time to write the patch during this CF,
feel free to bump it out.

David





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-07-07 07:12  Amit Langote <amitlangote09@gmail.com>
  parent: David Rowley <dgrowleyml@gmail.com>
  0 siblings, 0 replies; 97+ messages in thread

From: Amit Langote @ 2021-07-07 07:12 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Jul 7, 2021 at 1:41 PM David Rowley <dgrowleyml@gmail.com> wrote:
> On Fri, 2 Jul 2021 at 12:41, Amit Langote <amitlangote09@gmail.com> wrote:
> > I'll mark the CF entry as WoA, unless you'd rather I just mark it RwF.
>
> I've set it to waiting on author. It was still set to needs review.

Sorry it slipped my mind to do that and thanks.

> If you think you'll not get time to write the patch during this CF,
> feel free to bump it out.

I will try to post an update next week if not later this week,
hopefully with an updated patch.

-- 
Amit Langote
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-07-29 08:40  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  1 sibling, 1 reply; 97+ messages in thread

From: Amit Langote @ 2021-07-29 08:40 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Jul 2, 2021 at 9:40 AM Amit Langote <amitlangote09@gmail.com> wrote:
> On Fri, Jul 2, 2021 at 12:45 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > I think perhaps we ought to be more ambitious than that, and consider
> > separating the list of permissions-to-check from the rtable entirely.
> > Your patch hardly qualifies as non-invasive, plus it seems to invite
> > errors of omission, while if we changed the data structure altogether
> > then the compiler would help find any not-updated code.
> >
> > But the main reason that this strikes me as possibly a good idea
> > is that I was just taking another look at the complaint in [1],
> > where I wrote
> >
> > >> I think it's impossible to avoid less-than-O(N^2) growth on this sort
> > >> of case.  For example, the v2 subquery initially has RTEs for v2 itself
> > >> plus v1.  When we flatten v1 into v2, v2 acquires the RTEs from v1,
> > >> namely v1 itself plus foo.  Similarly, once vK-1 is pulled up into vK,
> > >> there are going to be order-of-K entries in vK's rtable, and that stacking
> > >> makes for O(N^2) work overall just in manipulating the rtable.
> > >>
> > >> We can't get rid of these rtable entries altogether, since all of them
> > >> represent table privilege checks that the executor will need to do.
> >
> > Perhaps, if we separated the rtable from the required-permissions data
> > structure, then we could avoid pulling up otherwise-useless RTEs when
> > flattening a view (or even better, not make the extra RTEs in the
> > first place??), and thus possibly avoid that exponential planning-time
> > growth for nested views.
> >
> > Or maybe not.  But I think we should take a hard look at whether
> > separating these data structures could solve both of these problems
> > at once.
>
> Ah, okay.  I'll think about decoupling the permission checking stuff
> from the range table data structure.

I have finished with the attached.  Sorry about the delay.

Think I've managed to get the first part done -- getting the
permission-checking info out of the range table -- but have not
seriously attempted the second -- doing away with the OLD/NEW range
table entries in the view/rule action queries, assuming that is what
you meant in the quoted.

One design point I think might need reconsidering is that the list of
the new RelPermissionInfo nodes that holds the permission-checking
info for relations has to be looked up with a linear search using the
relation OID, whereas it was basically free before if a particular of
code had the RTE handy.  Maybe I need to check if the overhead of that
is noticeable in some cases.

As there's not much time left in this CF, I've bumped the entry to the next one.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments:

v2-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 139.7K) download From 8db404d3c91a64573153a223878092b5a1eaac1f Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor, which actually checks the permissions, must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries in it, say, due to many partitions being added. This commit moves the permission checking information out of the range table entries into a new node type called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table, keyed on relation OIDs. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 75 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 +++++----- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 137 +++++++++---- src/backend/nodes/copyfuncs.c | 31 ++- src/backend/nodes/equalfuncs.c | 16 +- src/backend/nodes/outfuncs.c | 28 ++- src/backend/nodes/readfuncs.c | 22 ++- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 8 +- src/backend/optimizer/plan/subselect.c | 2 + src/backend/optimizer/prep/prepjointree.c | 26 +++ src/backend/optimizer/util/inherit.c | 169 +++++++++++----- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 61 ++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 223 ++++++++++++++-------- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 178 ++++++++--------- src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 ++-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 81 ++++---- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 970 insertions(+), 548 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 51fac77f3d..00e0b7ee37 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -457,7 +459,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -621,7 +624,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -655,12 +657,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1513,16 +1515,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1802,7 +1803,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1881,6 +1883,29 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + RelPermissionInfo *perminfo; + Oid relid; + Oid result; + + if (relInfo->ri_RootResultRelInfo) + relid = RelationGetRelid(relInfo->ri_RootResultRelInfo->ri_RelationDesc); + else + relid = RelationGetRelid(relInfo->ri_RelationDesc); + + perminfo = GetRelPermissionInfo(estate->es_relpermlist, relid, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1892,6 +1917,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1899,6 +1925,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1922,6 +1949,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1933,7 +1961,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2125,6 +2154,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2202,6 +2232,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2218,7 +2250,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2604,7 +2637,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2624,13 +2656,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3928,12 +3959,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3945,12 +3976,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 1f96e8b507..44ec89840f 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 19a3ffb7ff..036a4c97f2 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 32405f8610..85221ada52 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 64f54393f3..f5624eeab9 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 790f4ccb92..ffc45efb32 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 6b33951e0c..5118343f02 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 40a54ad0bd..4e9e94eee0 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index c14ca27c5e..75e4b0cbf3 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index fcd778c62a..63889d8875 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1173,7 +1173,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9506,7 +9507,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9648,7 +9650,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9834,7 +9837,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) table_close(pg_constraint, RowShareLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -9981,7 +9985,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -11890,7 +11895,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17573,7 +17579,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18459,7 +18466,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 4df05a0b33..5bfa730e8a 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b3ce4bae53..5bde876b78 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f8a4a40e7b..6c932b8261 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 5c723bc54e..f4456a1aca 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 6ef37c0886..7e649be151 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,32 +1252,76 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) +{ + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) + { + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; + + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); + } + + return resultRelInfo->ri_RootToChildMap; +} + /* Return a bitmap representing columns being inserted */ Bitmapset * ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) { /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. + * The columns are stored in estate->relpermlist. If this ResultRelInfo + * represents a child relation (a partition routing target of an INSERT or + * a child UPDATE target), it doesn't have an entry of its own, so fetch + * the parent's entry and map the columns to the order they are in the + * partition. */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->insertedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); + else + return perminfo->insertedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + return perminfo->insertedCols; } else { @@ -1295,22 +1340,28 @@ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->updatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); + else + return perminfo->updatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + return perminfo->updatedCols; } else return NULL; @@ -1321,22 +1372,28 @@ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); + else + return perminfo->extraUpdatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + return perminfo->extraUpdatedCols; } else return NULL; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 29020c908e..2f8864db6c 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -90,6 +90,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -776,6 +777,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1276,6 +1278,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2492,12 +2513,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3177,6 +3192,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5118,6 +5134,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 8a1762000c..7603c04784 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -985,6 +985,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2755,17 +2756,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3793,6 +3802,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 48202d2232..53319bc215 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -308,6 +308,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -702,6 +703,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -988,6 +990,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2263,6 +2280,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3072,6 +3090,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3304,12 +3323,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3991,6 +4004,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 77d082d8b4..7269b6c5e4 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -266,6 +266,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1505,13 +1506,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1590,6 +1602,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2075,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2847,6 +2861,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RTE", 3)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index d3f8639a40..888ea416d4 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4051,6 +4051,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5698,7 +5701,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 86816ffe19..644139011f 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -299,6 +300,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -486,6 +488,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -513,6 +516,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5772,6 +5776,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -5899,6 +5904,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index b145c5f45f..2a0876c6c9 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -260,6 +260,10 @@ set_plan_references(PlannerInfo *root, Plan *plan) */ add_rtes_to_flat_rtable(root, false); + /* Also add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); + /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list */ @@ -438,9 +442,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c9f7a09d10..5a622cabe5 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1504,6 +1504,8 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subselect->relpermlist); /* * And finally, build the JoinExpr node. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 224c5153b1..b1a1eec17f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1268,6 +1271,9 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) * Append child RTEs to parent rtable. */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); /* * Recursively scan the subquery's setOperations tree and add @@ -1311,6 +1317,7 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; + RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable); int childRTindex; AppendRelInfo *appinfo; @@ -1345,6 +1352,25 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, rtr->rtindex = childRTindex; (void) pull_up_subqueries_recurse(root, (Node *) rtr, NULL, NULL, appinfo); + + /* + * If the subquery was not pulled up, need to merge its relpermlist + * entries into the parent query by ourselves. It's a no-op if the + * subquery is not a simple one, because its relpermlist would be + * empty in that case. + * + * HACK: this relies on rte->subquery still being non-NULL to know + * that the subquery pull-up failed; it's set to NULL by + * pull_up_simple_subquery() when it succeeds. + */ + if (rte->rtekind == RTE_SUBQUERY && rte->subquery) + { + Query *subquery = rte->subquery; + + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); + } } else if (IsA(setOp, SetOperationStmt)) { diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 992ef87b9d..84a7941889 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte->relid, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +472,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +495,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +557,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +856,84 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, + false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index e105a4d5f1..d07c233aad 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 438b077004..4586d71548 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +858,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +874,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1061,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1354,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1589,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1836,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2286,6 +2295,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2352,6 +2362,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2370,7 +2381,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2382,7 +2393,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2423,8 +2434,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2711,6 +2722,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3180,9 +3192,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3232,9 +3252,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index b3f151d33b..d5537c424a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3242,16 +3242,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 7465919044..9ecbbb09f7 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte->relid, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo = NULL; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,93 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation OID and adds it into + * the provided list unless added already + * + * Returns the RelPermssionInfo. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, Oid relid) +{ + RelPermissionInfo *perminfo; + + /* Don't allow duplicate entries for a given relation. */ + perminfo = GetRelPermissionInfo(*relpermlist, relid, true); + if (perminfo) + return perminfo; + + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation OID in the + * provided list, erroring out or returning NULL (depending on + * missing_ok) if not found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok) +{ + ListCell *lc; + + Assert(OidIsValid(relid)); + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = lfirst(lc); + + if (perminfo->relid == relid) + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfo in the 'from' list to the 'into' list, + * "merging" the contents of any that are present in both. + * + * Returns the destination list. + */ +List * +MergeRelPermissionInfos(List *into, List *from) +{ + ListCell *l; + + foreach(l, from) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *target; + + target = AddRelPermissionInfo(&into, perminfo->relid); + + /* "merge" proprties. */ + target->inh = perminfo->inh; + target->requiredPerms |= perminfo->requiredPerms; + if (!OidIsValid(target->checkAsUser)) + target->checkAsUser = perminfo->checkAsUser; + target->selectedCols = bms_union(target->selectedCols, + perminfo->selectedCols); + target->insertedCols = bms_union(target->insertedCols, + perminfo->insertedCols); + target->updatedCols = bms_union(target->updatedCols, + perminfo->updatedCols); + target->extraUpdatedCols = bms_union(target->extraUpdatedCols, + perminfo->extraUpdatedCols); + } + + return into; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 6e8fbc4780..6d308b2cd8 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1142,7 +1142,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 675e400839..1af71073cd 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index b9a7a7ffbb..ef78cf1ca6 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -462,6 +463,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte->relid); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1617,7 +1620,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1657,7 +1660,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1667,14 +1670,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6589345ac4..5ffcb58306 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 54fd6d6fb2..92462daa61 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,25 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + sub_action->relpermlist = + MergeRelPermissionInfos(copyObject(parsetree->relpermlist), + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1566,16 +1569,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1593,9 +1598,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1680,8 +1685,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1722,18 +1726,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1822,26 +1814,6 @@ ApplyRetrieveRule(Query *parsetree, rte->tablesample = NULL; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1870,8 +1842,13 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3012,6 +2989,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3147,6 +3127,9 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, + base_rte->relid, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3219,51 +3202,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + view_rte->relid, false); + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, + new_rte->relid); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3367,7 +3352,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3378,8 +3363,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3653,6 +3636,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3664,6 +3648,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + rt_entry->relid, false); /* * We can use NoLock here since either the parser or @@ -3749,7 +3735,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index e10f94904e..9da976e375 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte->relid, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 2e55913bc8..f438b71229 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1555,6 +1556,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1602,10 +1604,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 96269fc2ad..486fedfd43 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1308,8 +1308,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1327,32 +1327,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1362,9 +1356,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 0c8c05f6c2..15bb356572 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5135,7 +5135,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5187,7 +5188,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5271,7 +5273,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5319,7 +5322,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5380,6 +5384,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5388,7 +5393,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5457,7 +5463,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 778fa27fd1..f3ce859395 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 4d68d9cceb..752e204ec7 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 3dc03c913e..19ed90b956 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 37cb4f3d59..5d44c88238 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -522,6 +522,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -563,6 +571,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f7b009ec43..e7a346ac42 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index e28248af32..e89ce12998 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -934,37 +936,6 @@ typedef struct PartitionCmd * needed by ruleutils.c to determine whether RTEs should be shown in * decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1142,14 +1113,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index e20c245f98..2154ac42e3 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -100,6 +100,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -725,7 +727,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 98a4c73f93..b92b730b7d 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -633,6 +637,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index e9472f2f73..1ec96d89bd 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 9a15de5025..5b884ad96f 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 1500de2dd0..dd4b751f73 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 8336c2c5a2..ae06487670 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, Oid relid); +extern List *MergeRelPermissionInfos(List *into, List *from); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 728a60c0b0..26300cc143 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-08-20 13:46  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2021-08-20 13:46 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Jul 29, 2021 at 5:40 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Fri, Jul 2, 2021 at 9:40 AM Amit Langote <amitlangote09@gmail.com> wrote:
> > On Fri, Jul 2, 2021 at 12:45 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > > Perhaps, if we separated the rtable from the required-permissions data
> > > structure, then we could avoid pulling up otherwise-useless RTEs when
> > > flattening a view (or even better, not make the extra RTEs in the
> > > first place??), and thus possibly avoid that exponential planning-time
> > > growth for nested views.
>
> Think I've managed to get the first part done -- getting the
> permission-checking info out of the range table -- but have not
> seriously attempted the second -- doing away with the OLD/NEW range
> table entries in the view/rule action queries, assuming that is what
> you meant in the quoted.

I took a stab at the 2nd part, implemented in the attached 0002.

The patch removes UpdateRangeTableOfViewParse() which would add the
dummy OLD/NEW entries to a view rule's action query's rtable, citing
these reasons:

- * These extra RT entries are not actually used in the query,
- * except for run-time locking and permission checking.

0001 makes them unnecessary for permission checking.  Though, a
RELATION-kind RTE still be must be present in the rtable for run-time
locking, so I adjusted ApplyRetrieveRule() as follows:

@@ -1803,16 +1804,26 @@ ApplyRetrieveRule(Query *parsetree,
     * original RTE to a subquery RTE.
     */
    rte = rt_fetch(rt_index, parsetree->rtable);
+   subquery_rte = rte;

-   rte->rtekind = RTE_SUBQUERY;
-   rte->subquery = rule_action;
-   rte->security_barrier = RelationIsSecurityView(relation);
+   /*
+    * Before modifying, store a copy of itself so as to serve as the entry
+    * to be used by the executor to lock the view relation and for the
+    * planner to be able to record the view relation OID in the PlannedStmt
+    * that it produces for the query.
+    */
+   rte = copyObject(rte);
+   parsetree->rtable = lappend(parsetree->rtable, rte);
+
+   subquery_rte->rtekind = RTE_SUBQUERY;
+   subquery_rte->subquery = rule_action;
+   subquery_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->inh = false;           /* must not be set for a subquery */
+   subquery_rte->relid = InvalidOid;
+   subquery_rte->relkind = 0;
+   subquery_rte->rellockmode = 0;
+   subquery_rte->tablesample = NULL;
+   subquery_rte->inh = false;          /* must not be set for a subquery */

    return parsetree;
 }

Outputs for a bunch of regression tests needed to be adjusted to
account for that pg_get_viewdef() no longer qualifies view column
names in the deparsed queries, that is, if they reference only a
single relation.   Previously, those dummy OLD/NEW entries tricked
make_ruledef(), get_query_def() et al into setting
deparse_context.varprefix to true.  contrib/postgre_fdw test output
likewise needed adjustment due to its deparse code being impacted by
those dummy entries no longer being present, I believe.

I haven't yet checked how this further improves the performance for
the case discussed at [1] that prompted this.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

[1] https://www.postgresql.org/message-id/flat/797aff54-b49b-4914-9ff9-aa42564a4d7d%40www.fastmail.com

Attachments: download all (2 files, .tar.gz)

v3-0002-Remove-UpdateRangeTableOfViewParse.patch (application/octet-stream, 111.6K) download From 62f63bc271483050a7a38f12698102a28956ea15 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v3 2/2] Remove UpdateRangeTableOfViewParse() --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 29 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 12 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 778 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- 21 files changed, 722 insertions(+), 789 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index e3ee30f1aa..5ed3c155cd 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2478,7 +2478,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2541,7 +2541,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6346,10 +6346,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6437,10 +6437,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 5bfa730e8a..e596683f27 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -323,78 +323,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -557,12 +485,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 92462daa61..1002f1dc11 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1685,7 +1685,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1803,16 +1804,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index a4ee54d516..03bc29a046 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2081,7 +2081,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2097,7 +2097,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2113,7 +2113,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2129,7 +2129,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2147,7 +2147,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -2952,7 +2952,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 5949996ebc..57df8f7889 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1535,7 +1535,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1587,7 +1587,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1603,7 +1603,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1619,7 +1619,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2104,15 +2104,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 8dcb00ac67..8c2f75f29f 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2414,8 +2414,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2426,8 +2426,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2453,8 +2453,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2466,8 +2466,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f06ae543e4..5f4c5c0db6 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index f50ef76685..dee7928ba2 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -341,10 +341,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -386,9 +386,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -402,9 +402,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -418,9 +418,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -434,9 +434,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -451,9 +451,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -467,9 +467,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -483,9 +483,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -499,9 +499,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -516,9 +516,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -532,9 +532,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -548,9 +548,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -564,9 +564,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -581,9 +581,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -597,9 +597,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -613,9 +613,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -629,9 +629,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -647,9 +647,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -663,9 +663,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -679,9 +679,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -695,9 +695,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -714,9 +714,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -730,9 +730,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -746,9 +746,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -762,9 +762,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1251,10 +1251,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1531,9 +1531,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1550,9 +1550,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1573,9 +1573,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1665,8 +1665,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1895,7 +1895,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -1947,19 +1947,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 7b6b0bb4f9..2b85967c1f 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -179,12 +179,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 4c467c1b15..71d6ec7034 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index b75afcc01a..493ad70db8 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -633,10 +633,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -648,10 +648,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -666,10 +666,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -680,10 +680,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index cafca1f9ae..6347c62774 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 2fa00a3c29..fefad636ca 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1291,21 +1291,21 @@ iexit| SELECT ih.name, FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); -key_dependent_view| SELECT view_base_table.key, - view_base_table.data +key_dependent_view| SELECT key, + data FROM view_base_table - GROUP BY view_base_table.key; + GROUP BY key; key_dependent_view_no_cols| SELECT FROM view_base_table - GROUP BY view_base_table.key - HAVING (length((view_base_table.data)::text) > 0); -mvtest_tv| SELECT mvtest_t.type, - sum(mvtest_t.amt) AS totamt + GROUP BY key + HAVING (length((data)::text) > 0); +mvtest_tv| SELECT type, + sum(amt) AS totamt FROM mvtest_t - GROUP BY mvtest_t.type; -mvtest_tvv| SELECT sum(mvtest_tv.totamt) AS grandtot + GROUP BY type; +mvtest_tvv| SELECT sum(totamt) AS grandtot FROM mvtest_tv; -mvtest_tvvmv| SELECT mvtest_tvvm.grandtot +mvtest_tvvmv| SELECT grandtot FROM mvtest_tvvm; pg_available_extension_versions| SELECT e.name, e.version, @@ -1324,50 +1324,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1380,22 +1380,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1435,13 +1435,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1459,10 +1459,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1708,23 +1708,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1738,10 +1738,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1809,13 +1809,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1828,57 +1828,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1901,8 +1901,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1911,13 +1911,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2094,41 +2094,41 @@ pg_stat_subscription| SELECT su.oid AS subid, st.latest_end_time FROM (pg_subscription su LEFT JOIN pg_stat_get_subscription(NULL::oid) st(subid, relid, pid, received_lsn, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time) ON ((st.subid = su.oid))); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2138,68 +2138,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2216,19 +2216,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2238,19 +2238,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2289,64 +2289,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2529,24 +2529,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -2572,26 +2572,26 @@ pg_views| SELECT n.nspname AS schemaname, FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); -rtest_v1| SELECT rtest_t1.a, - rtest_t1.b +rtest_v1| SELECT a, + b FROM rtest_t1; rtest_vcomp| SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); -rtest_vview1| SELECT x.a, - x.b +rtest_vview1| SELECT a, + b FROM rtest_view1 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); -rtest_vview2| SELECT rtest_view1.a, - rtest_view1.b +rtest_vview2| SELECT a, + b FROM rtest_view1 - WHERE rtest_view1.v; -rtest_vview3| SELECT x.a, - x.b + WHERE v; +rtest_vview3| SELECT a, + b FROM rtest_vview2 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y @@ -2603,9 +2603,9 @@ rtest_vview4| SELECT x.a, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; -rtest_vview5| SELECT rtest_view1.a, - rtest_view1.b, - rtest_viewfunc1(rtest_view1.a) AS refcount +rtest_vview5| SELECT a, + b, + rtest_viewfunc1(a) AS refcount FROM rtest_view1; shoe| SELECT sh.shoename, sh.sh_avail, @@ -2635,20 +2635,20 @@ shoelace| SELECT s.sl_name, FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); -shoelace_candelete| SELECT shoelace_obsolete.sl_name, - shoelace_obsolete.sl_avail, - shoelace_obsolete.sl_color, - shoelace_obsolete.sl_len, - shoelace_obsolete.sl_unit, - shoelace_obsolete.sl_len_cm +shoelace_candelete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace_obsolete - WHERE (shoelace_obsolete.sl_avail = 0); -shoelace_obsolete| SELECT shoelace.sl_name, - shoelace.sl_avail, - shoelace.sl_color, - shoelace.sl_len, - shoelace.sl_unit, - shoelace.sl_len_cm + WHERE (sl_avail = 0); +shoelace_obsolete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace WHERE (NOT (EXISTS ( SELECT shoe.shoename FROM shoe @@ -2659,14 +2659,14 @@ street| SELECT r.name, FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); -test_tablesample_v1| SELECT test_tablesample.id +test_tablesample_v1| SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); -test_tablesample_v2| SELECT test_tablesample.id +test_tablesample_v2| SELECT id FROM test_tablesample TABLESAMPLE system (99); -toyemp| SELECT emp.name, - emp.age, - emp.location, - (12 * emp.salary) AS annualsal +toyemp| SELECT name, + age, + location, + (12 * salary) AS annualsal FROM emp; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') @@ -3256,7 +3256,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3295,8 +3295,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3308,8 +3308,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3321,8 +3321,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3334,8 +3334,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 078358d226..15a64aca0c 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 5d124cf96f..ed0f85e113 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1251,8 +1251,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index cdff914b93..a026946fb4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1666,19 +1666,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1719,17 +1719,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1759,17 +1759,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -1800,16 +1800,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -1831,15 +1831,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 3523a7dcc1..d262a7944e 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -802,9 +802,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1291,9 +1291,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1313,9 +1313,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) -- 2.24.1
v3-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 139.8K) download From f24664b587479893cb8ea44e714c981b85bcabf1 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v3 1/2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor, which actually checks the permissions, must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries in it, say, due to many partitions being added. This commit moves the permission checking information out of the range table entries into a new node type called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table, keyed on relation OIDs. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 75 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 +++++----- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 137 +++++++++---- src/backend/nodes/copyfuncs.c | 31 ++- src/backend/nodes/equalfuncs.c | 16 +- src/backend/nodes/outfuncs.c | 28 ++- src/backend/nodes/readfuncs.c | 22 ++- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 8 +- src/backend/optimizer/plan/subselect.c | 2 + src/backend/optimizer/prep/prepjointree.c | 26 +++ src/backend/optimizer/util/inherit.c | 169 +++++++++++----- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 61 ++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 223 ++++++++++++++-------- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 178 ++++++++--------- src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 ++-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 81 ++++---- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 970 insertions(+), 548 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 9d443baf02..39d28df3b6 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -457,7 +459,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -622,7 +625,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -656,12 +658,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1514,16 +1516,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1803,7 +1804,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1882,6 +1884,29 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + RelPermissionInfo *perminfo; + Oid relid; + Oid result; + + if (relInfo->ri_RootResultRelInfo) + relid = RelationGetRelid(relInfo->ri_RootResultRelInfo->ri_RelationDesc); + else + relid = RelationGetRelid(relInfo->ri_RelationDesc); + + perminfo = GetRelPermissionInfo(estate->es_relpermlist, relid, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1893,6 +1918,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1900,6 +1926,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1923,6 +1950,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1934,7 +1962,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2126,6 +2155,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2203,6 +2233,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2219,7 +2251,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2605,7 +2638,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2625,13 +2657,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3929,12 +3960,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3946,12 +3977,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 1f96e8b507..44ec89840f 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 19a3ffb7ff..036a4c97f2 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 32405f8610..85221ada52 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 64f54393f3..f5624eeab9 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 790f4ccb92..ffc45efb32 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 6b33951e0c..5118343f02 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 40a54ad0bd..4e9e94eee0 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index c14ca27c5e..75e4b0cbf3 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index a03077139d..d46c89cfbf 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1172,7 +1172,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9463,7 +9464,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9605,7 +9607,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9791,7 +9794,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) table_close(pg_constraint, RowShareLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -9938,7 +9942,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -11847,7 +11852,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17530,7 +17536,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18416,7 +18423,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 4df05a0b33..5bfa730e8a 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b3ce4bae53..5bde876b78 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f8a4a40e7b..6c932b8261 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 5c723bc54e..f4456a1aca 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 6ef37c0886..7e649be151 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,32 +1252,76 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) +{ + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) + { + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; + + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); + } + + return resultRelInfo->ri_RootToChildMap; +} + /* Return a bitmap representing columns being inserted */ Bitmapset * ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) { /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. + * The columns are stored in estate->relpermlist. If this ResultRelInfo + * represents a child relation (a partition routing target of an INSERT or + * a child UPDATE target), it doesn't have an entry of its own, so fetch + * the parent's entry and map the columns to the order they are in the + * partition. */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->insertedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); + else + return perminfo->insertedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + return perminfo->insertedCols; } else { @@ -1295,22 +1340,28 @@ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->updatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); + else + return perminfo->updatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + return perminfo->updatedCols; } else return NULL; @@ -1321,22 +1372,28 @@ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); + else + return perminfo->extraUpdatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + return perminfo->extraUpdatedCols; } else return NULL; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 38251c2b8e..db56906d17 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -776,6 +777,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1264,6 +1266,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2480,12 +2501,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3165,6 +3180,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5106,6 +5122,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 8a1762000c..7603c04784 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -985,6 +985,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2755,17 +2756,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3793,6 +3802,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 87561cbb6f..2d3c239e13 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -308,6 +308,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -702,6 +703,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -988,6 +990,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2263,6 +2280,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3073,6 +3091,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3305,12 +3324,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3992,6 +4005,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 0dd1ad7dfc..d8e30b8dbb 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -266,6 +266,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1505,13 +1506,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1590,6 +1602,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2075,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2847,6 +2861,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RTE", 3)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index a5f6d678cc..700643d2c4 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4051,6 +4051,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5698,7 +5701,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 2cd691191c..485f78da93 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -299,6 +300,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -486,6 +488,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -513,6 +516,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5772,6 +5776,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -5899,6 +5904,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index e50624c465..b519c53f70 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -260,6 +260,10 @@ set_plan_references(PlannerInfo *root, Plan *plan) */ add_rtes_to_flat_rtable(root, false); + /* Also add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); + /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list */ @@ -438,9 +442,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c9f7a09d10..5a622cabe5 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1504,6 +1504,8 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subselect->relpermlist); /* * And finally, build the JoinExpr node. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 224c5153b1..b1a1eec17f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1268,6 +1271,9 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) * Append child RTEs to parent rtable. */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); /* * Recursively scan the subquery's setOperations tree and add @@ -1311,6 +1317,7 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; + RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable); int childRTindex; AppendRelInfo *appinfo; @@ -1345,6 +1352,25 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, rtr->rtindex = childRTindex; (void) pull_up_subqueries_recurse(root, (Node *) rtr, NULL, NULL, appinfo); + + /* + * If the subquery was not pulled up, need to merge its relpermlist + * entries into the parent query by ourselves. It's a no-op if the + * subquery is not a simple one, because its relpermlist would be + * empty in that case. + * + * HACK: this relies on rte->subquery still being non-NULL to know + * that the subquery pull-up failed; it's set to NULL by + * pull_up_simple_subquery() when it succeeds. + */ + if (rte->rtekind == RTE_SUBQUERY && rte->subquery) + { + Query *subquery = rte->subquery; + + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); + } } else if (IsA(setOp, SetOperationStmt)) { diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index c758172efa..282c754cc6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte->relid, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +472,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +495,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +557,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +856,84 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, + false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 47769cea45..6c7eeeb419 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 15669c8238..2d929f097b 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +858,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +874,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1061,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1354,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1589,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1836,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2286,6 +2295,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2352,6 +2362,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2370,7 +2381,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2382,7 +2393,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2423,8 +2434,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2711,6 +2722,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3189,9 +3201,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3247,9 +3267,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index b3f151d33b..d5537c424a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3242,16 +3242,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 7465919044..9ecbbb09f7 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte->relid, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo = NULL; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,93 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation OID and adds it into + * the provided list unless added already + * + * Returns the RelPermssionInfo. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, Oid relid) +{ + RelPermissionInfo *perminfo; + + /* Don't allow duplicate entries for a given relation. */ + perminfo = GetRelPermissionInfo(*relpermlist, relid, true); + if (perminfo) + return perminfo; + + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation OID in the + * provided list, erroring out or returning NULL (depending on + * missing_ok) if not found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok) +{ + ListCell *lc; + + Assert(OidIsValid(relid)); + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = lfirst(lc); + + if (perminfo->relid == relid) + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfo in the 'from' list to the 'into' list, + * "merging" the contents of any that are present in both. + * + * Returns the destination list. + */ +List * +MergeRelPermissionInfos(List *into, List *from) +{ + ListCell *l; + + foreach(l, from) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *target; + + target = AddRelPermissionInfo(&into, perminfo->relid); + + /* "merge" proprties. */ + target->inh = perminfo->inh; + target->requiredPerms |= perminfo->requiredPerms; + if (!OidIsValid(target->checkAsUser)) + target->checkAsUser = perminfo->checkAsUser; + target->selectedCols = bms_union(target->selectedCols, + perminfo->selectedCols); + target->insertedCols = bms_union(target->insertedCols, + perminfo->insertedCols); + target->updatedCols = bms_union(target->updatedCols, + perminfo->updatedCols); + target->extraUpdatedCols = bms_union(target->extraUpdatedCols, + perminfo->extraUpdatedCols); + } + + return into; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 6e8fbc4780..6d308b2cd8 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1142,7 +1142,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 675e400839..1af71073cd 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 38b493e4f5..db1b1f1002 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -463,6 +464,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte->relid); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1691,7 +1694,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1731,7 +1734,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1741,14 +1744,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6589345ac4..5ffcb58306 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 54fd6d6fb2..92462daa61 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,25 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + sub_action->relpermlist = + MergeRelPermissionInfos(copyObject(parsetree->relpermlist), + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1566,16 +1569,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1593,9 +1598,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1680,8 +1685,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1722,18 +1726,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1822,26 +1814,6 @@ ApplyRetrieveRule(Query *parsetree, rte->tablesample = NULL; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1870,8 +1842,13 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3012,6 +2989,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3147,6 +3127,9 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, + base_rte->relid, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3219,51 +3202,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + view_rte->relid, false); + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, + new_rte->relid); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3367,7 +3352,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3378,8 +3363,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3653,6 +3636,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3664,6 +3648,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + rt_entry->relid, false); /* * We can use NoLock here since either the parser or @@ -3749,7 +3735,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index e10f94904e..9da976e375 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte->relid, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 2e55913bc8..f438b71229 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1555,6 +1556,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1602,10 +1604,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 96269fc2ad..486fedfd43 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1308,8 +1308,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1327,32 +1327,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1362,9 +1356,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 0c8c05f6c2..15bb356572 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5135,7 +5135,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5187,7 +5188,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5271,7 +5273,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5319,7 +5322,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5380,6 +5384,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5388,7 +5393,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5457,7 +5463,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 778fa27fd1..f3ce859395 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 4d68d9cceb..752e204ec7 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 3dc03c913e..19ed90b956 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 37cb4f3d59..5d44c88238 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -522,6 +522,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -563,6 +571,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6a4d82f0a8..ee4fcdf32f 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 7af13dee43..a02ca46ce6 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -934,37 +936,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1142,14 +1113,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 6e068f2c8b..09f084a57d 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -100,6 +100,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -725,7 +727,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index ec9a8b0c81..7b39ca264a 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -636,6 +640,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index e9472f2f73..1ec96d89bd 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 9a15de5025..5b884ad96f 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 1500de2dd0..dd4b751f73 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 8336c2c5a2..ae06487670 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, Oid relid); +extern List *MergeRelPermissionInfos(List *into, List *from); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 728a60c0b0..26300cc143 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-08-26 09:13  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2021-08-26 09:13 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Aug 20, 2021 at 10:46 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Thu, Jul 29, 2021 at 5:40 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > On Fri, Jul 2, 2021 at 9:40 AM Amit Langote <amitlangote09@gmail.com> wrote:
> > > On Fri, Jul 2, 2021 at 12:45 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > > > Perhaps, if we separated the rtable from the required-permissions data
> > > > structure, then we could avoid pulling up otherwise-useless RTEs when
> > > > flattening a view (or even better, not make the extra RTEs in the
> > > > first place??), and thus possibly avoid that exponential planning-time
> > > > growth for nested views.
> >
> > Think I've managed to get the first part done -- getting the
> > permission-checking info out of the range table -- but have not
> > seriously attempted the second -- doing away with the OLD/NEW range
> > table entries in the view/rule action queries, assuming that is what
> > you meant in the quoted.
>
> I took a stab at the 2nd part, implemented in the attached 0002.
>
> The patch removes UpdateRangeTableOfViewParse() which would add the
> dummy OLD/NEW entries to a view rule's action query's rtable
>
> I haven't yet checked how this further improves the performance for
> the case discussed at [1] that prompted this.
>
> [1] https://www.postgresql.org/message-id/flat/797aff54-b49b-4914-9ff9-aa42564a4d7d%40www.fastmail.com

I checked the time required to do explain select * from v512 (worst
case), using the setup described at the above link and I get the
following numbers:

HEAD: 119.774 ms
0001  : 129.802 ms
0002  : 109.456 ms

So it appears that applying only 0001 makes things a bit worse for
this case.  That seems to have to do with the following addition in
pull_up_simple_subquery():

@@ -1131,6 +1131,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node
*jtnode, RangeTblEntry *rte,
     */
    parse->rtable = list_concat(parse->rtable, subquery->rtable);

+   parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist,
+                                                subquery->relpermlist);
+

What it does is pull up the RelPermissionInfo nodes in the subquery
being pulled up into the parent query and it's not a simple
list_concat(), because I decided that it's better to de-duplicate the
entries for a given relation OID even across subqueries.

Things get better than HEAD with 0002, because less work needs to be
done in the rewriter when copying the subqueries into the main query,
especially the range table, which only has 1 entry now, not 3 per
view.

Attached updated patches.  I wrote a longer commit message for 0002 this time.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v4-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 140.3K) download From 624d523d975c2306a88dd03a73540b9ba313805d Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v4 1/2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries that belong to inheritance child tables whose permissions need not be checked. This commit moves the permission checking information out of the range table entries into a new node type called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table, keyed on relation OIDs. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 75 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 +++++----- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 137 +++++++++---- src/backend/nodes/copyfuncs.c | 31 ++- src/backend/nodes/equalfuncs.c | 16 +- src/backend/nodes/outfuncs.c | 28 ++- src/backend/nodes/readfuncs.c | 22 ++- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 8 +- src/backend/optimizer/plan/subselect.c | 2 + src/backend/optimizer/prep/prepjointree.c | 26 +++ src/backend/optimizer/util/inherit.c | 172 ++++++++++++----- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 61 ++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 223 ++++++++++++++-------- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 178 ++++++++--------- src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 ++-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 81 ++++---- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 971 insertions(+), 550 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 9d443baf02..39d28df3b6 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -457,7 +459,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -622,7 +625,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -656,12 +658,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1514,16 +1516,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1803,7 +1804,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1882,6 +1884,29 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + RelPermissionInfo *perminfo; + Oid relid; + Oid result; + + if (relInfo->ri_RootResultRelInfo) + relid = RelationGetRelid(relInfo->ri_RootResultRelInfo->ri_RelationDesc); + else + relid = RelationGetRelid(relInfo->ri_RelationDesc); + + perminfo = GetRelPermissionInfo(estate->es_relpermlist, relid, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1893,6 +1918,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1900,6 +1926,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1923,6 +1950,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1934,7 +1962,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2126,6 +2155,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2203,6 +2233,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2219,7 +2251,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2605,7 +2638,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2625,13 +2657,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3929,12 +3960,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3946,12 +3977,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 1f96e8b507..44ec89840f 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 19a3ffb7ff..036a4c97f2 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 32405f8610..85221ada52 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 64f54393f3..f5624eeab9 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 790f4ccb92..ffc45efb32 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 6b33951e0c..5118343f02 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 40a54ad0bd..4e9e94eee0 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index c14ca27c5e..75e4b0cbf3 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dbee6ae199..7f71f5cfb3 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1172,7 +1172,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9494,7 +9495,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9636,7 +9638,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9822,7 +9825,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) table_close(pg_constraint, RowShareLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -9969,7 +9973,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -11878,7 +11883,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17561,7 +17567,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18447,7 +18454,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 4df05a0b33..5bfa730e8a 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b3ce4bae53..5bde876b78 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f8a4a40e7b..6c932b8261 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 5c723bc54e..f4456a1aca 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 6ef37c0886..7e649be151 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,32 +1252,76 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) +{ + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) + { + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; + + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); + } + + return resultRelInfo->ri_RootToChildMap; +} + /* Return a bitmap representing columns being inserted */ Bitmapset * ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) { /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. + * The columns are stored in estate->relpermlist. If this ResultRelInfo + * represents a child relation (a partition routing target of an INSERT or + * a child UPDATE target), it doesn't have an entry of its own, so fetch + * the parent's entry and map the columns to the order they are in the + * partition. */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->insertedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); + else + return perminfo->insertedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + return perminfo->insertedCols; } else { @@ -1295,22 +1340,28 @@ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->updatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); + else + return perminfo->updatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + return perminfo->updatedCols; } else return NULL; @@ -1321,22 +1372,28 @@ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); + else + return perminfo->extraUpdatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + return perminfo->extraUpdatedCols; } else return NULL; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 38251c2b8e..db56906d17 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -776,6 +777,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1264,6 +1266,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2480,12 +2501,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3165,6 +3180,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5106,6 +5122,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 8a1762000c..7603c04784 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -985,6 +985,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2755,17 +2756,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3793,6 +3802,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 87561cbb6f..2d3c239e13 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -308,6 +308,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -702,6 +703,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -988,6 +990,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2263,6 +2280,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3073,6 +3091,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3305,12 +3324,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3992,6 +4005,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 0dd1ad7dfc..d8e30b8dbb 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -266,6 +266,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1505,13 +1506,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1590,6 +1602,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2075,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2847,6 +2861,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RTE", 3)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index a5f6d678cc..700643d2c4 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4051,6 +4051,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5698,7 +5701,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 1e42d75465..55505bfee3 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5925,6 +5929,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6052,6 +6057,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index e50624c465..b519c53f70 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -260,6 +260,10 @@ set_plan_references(PlannerInfo *root, Plan *plan) */ add_rtes_to_flat_rtable(root, false); + /* Also add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); + /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list */ @@ -438,9 +442,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c9f7a09d10..5a622cabe5 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1504,6 +1504,8 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subselect->relpermlist); /* * And finally, build the JoinExpr node. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 224c5153b1..b1a1eec17f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1268,6 +1271,9 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) * Append child RTEs to parent rtable. */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); /* * Recursively scan the subquery's setOperations tree and add @@ -1311,6 +1317,7 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; + RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable); int childRTindex; AppendRelInfo *appinfo; @@ -1345,6 +1352,25 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, rtr->rtindex = childRTindex; (void) pull_up_subqueries_recurse(root, (Node *) rtr, NULL, NULL, appinfo); + + /* + * If the subquery was not pulled up, need to merge its relpermlist + * entries into the parent query by ourselves. It's a no-op if the + * subquery is not a simple one, because its relpermlist would be + * empty in that case. + * + * HACK: this relies on rte->subquery still being non-NULL to know + * that the subquery pull-up failed; it's set to NULL by + * pull_up_simple_subquery() when it succeeds. + */ + if (rte->rtekind == RTE_SUBQUERY && rte->subquery) + { + Query *subquery = rte->subquery; + + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); + } } else if (IsA(setOp, SetOperationStmt)) { diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index c758172efa..49d707da5e 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte->relid, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,84 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, + false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 47769cea45..6c7eeeb419 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 15669c8238..2d929f097b 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +858,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +874,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1061,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1354,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1589,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1836,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2286,6 +2295,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2352,6 +2362,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2370,7 +2381,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2382,7 +2393,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2423,8 +2434,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2711,6 +2722,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3189,9 +3201,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3247,9 +3267,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index b3f151d33b..d5537c424a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3242,16 +3242,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 7465919044..9ecbbb09f7 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte->relid, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo = NULL; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,93 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation OID and adds it into + * the provided list unless added already + * + * Returns the RelPermssionInfo. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, Oid relid) +{ + RelPermissionInfo *perminfo; + + /* Don't allow duplicate entries for a given relation. */ + perminfo = GetRelPermissionInfo(*relpermlist, relid, true); + if (perminfo) + return perminfo; + + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation OID in the + * provided list, erroring out or returning NULL (depending on + * missing_ok) if not found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok) +{ + ListCell *lc; + + Assert(OidIsValid(relid)); + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = lfirst(lc); + + if (perminfo->relid == relid) + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfo in the 'from' list to the 'into' list, + * "merging" the contents of any that are present in both. + * + * Returns the destination list. + */ +List * +MergeRelPermissionInfos(List *into, List *from) +{ + ListCell *l; + + foreach(l, from) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *target; + + target = AddRelPermissionInfo(&into, perminfo->relid); + + /* "merge" proprties. */ + target->inh = perminfo->inh; + target->requiredPerms |= perminfo->requiredPerms; + if (!OidIsValid(target->checkAsUser)) + target->checkAsUser = perminfo->checkAsUser; + target->selectedCols = bms_union(target->selectedCols, + perminfo->selectedCols); + target->insertedCols = bms_union(target->insertedCols, + perminfo->insertedCols); + target->updatedCols = bms_union(target->updatedCols, + perminfo->updatedCols); + target->extraUpdatedCols = bms_union(target->extraUpdatedCols, + perminfo->extraUpdatedCols); + } + + return into; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 6e8fbc4780..6d308b2cd8 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1142,7 +1142,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e5eefdbd43..a0842bf3a5 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 38b493e4f5..db1b1f1002 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -463,6 +464,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte->relid); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1691,7 +1694,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1731,7 +1734,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1741,14 +1744,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6589345ac4..5ffcb58306 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 54fd6d6fb2..92462daa61 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,25 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + sub_action->relpermlist = + MergeRelPermissionInfos(copyObject(parsetree->relpermlist), + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1566,16 +1569,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1593,9 +1598,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1680,8 +1685,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1722,18 +1726,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1822,26 +1814,6 @@ ApplyRetrieveRule(Query *parsetree, rte->tablesample = NULL; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1870,8 +1842,13 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3012,6 +2989,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3147,6 +3127,9 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, + base_rte->relid, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3219,51 +3202,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + view_rte->relid, false); + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, + new_rte->relid); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3367,7 +3352,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3378,8 +3363,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3653,6 +3636,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3664,6 +3648,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + rt_entry->relid, false); /* * We can use NoLock here since either the parser or @@ -3749,7 +3735,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index e10f94904e..9da976e375 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte->relid, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 2e55913bc8..f438b71229 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1555,6 +1556,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1602,10 +1604,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 96269fc2ad..486fedfd43 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1308,8 +1308,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1327,32 +1327,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1362,9 +1356,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 0c8c05f6c2..15bb356572 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5135,7 +5135,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5187,7 +5188,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5271,7 +5273,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5319,7 +5322,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5380,6 +5384,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5388,7 +5393,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5457,7 +5463,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 778fa27fd1..f3ce859395 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 4d68d9cceb..752e204ec7 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 3dc03c913e..19ed90b956 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 37cb4f3d59..5d44c88238 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -522,6 +522,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -563,6 +571,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6a4d82f0a8..ee4fcdf32f 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 7af13dee43..a02ca46ce6 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -934,37 +936,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1142,14 +1113,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1abe233db2..244b5c5792 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -726,7 +728,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index ec9a8b0c81..7b39ca264a 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -636,6 +640,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index e9472f2f73..1ec96d89bd 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 9a15de5025..5b884ad96f 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 1500de2dd0..dd4b751f73 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 8336c2c5a2..ae06487670 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, Oid relid); +extern List *MergeRelPermissionInfos(List *into, List *from); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 728a60c0b0..26300cc143 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
v4-0002-Do-not-add-OLD-NEW-RTEs-to-stored-view-rule-actio.patch (application/octet-stream, 114.7K) download From cdf76be568e0eeb057e1aa24b39ee912fdcbb315 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v4 2/2] Do not add OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes those RTEs unnecessary for that purpose. Also, this commit teaches the rewriter to add an RTE for any view relations referenced in a query so that they are locked during execution. The same RTE also ensures that the view relation OIDs are correctly reported into PlannedStmt.relationOids. As this changes the shape of the view queries stored in the catalog, a bunch of regression tests that display those queries now display them slightly differently, so their outputs have been updated. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 29 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 12 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 778 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- 22 files changed, 734 insertions(+), 801 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index e3ee30f1aa..5ed3c155cd 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2478,7 +2478,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2541,7 +2541,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6346,10 +6346,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6437,10 +6437,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 5bfa730e8a..e596683f27 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -323,78 +323,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -557,12 +485,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 92462daa61..1002f1dc11 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1685,7 +1685,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1803,16 +1804,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index a4ee54d516..03bc29a046 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2081,7 +2081,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2097,7 +2097,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2113,7 +2113,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2129,7 +2129,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2147,7 +2147,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -2952,7 +2952,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 5949996ebc..57df8f7889 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1535,7 +1535,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1587,7 +1587,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1603,7 +1603,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1619,7 +1619,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2104,15 +2104,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 4bee0c1173..cfa69aab78 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2414,8 +2414,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2426,8 +2426,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2453,8 +2453,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2466,8 +2466,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 70133df804..4748db0305 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f06ae543e4..5f4c5c0db6 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index f50ef76685..dee7928ba2 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -341,10 +341,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -386,9 +386,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -402,9 +402,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -418,9 +418,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -434,9 +434,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -451,9 +451,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -467,9 +467,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -483,9 +483,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -499,9 +499,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -516,9 +516,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -532,9 +532,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -548,9 +548,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -564,9 +564,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -581,9 +581,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -597,9 +597,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -613,9 +613,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -629,9 +629,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -647,9 +647,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -663,9 +663,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -679,9 +679,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -695,9 +695,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -714,9 +714,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -730,9 +730,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -746,9 +746,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -762,9 +762,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1251,10 +1251,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1531,9 +1531,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1550,9 +1550,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1573,9 +1573,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1665,8 +1665,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1895,7 +1895,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -1947,19 +1947,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 7b6b0bb4f9..2b85967c1f 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -179,12 +179,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 4c467c1b15..71d6ec7034 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index b75afcc01a..493ad70db8 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -633,10 +633,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -648,10 +648,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -666,10 +666,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -680,10 +680,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index cafca1f9ae..6347c62774 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 2fa00a3c29..fefad636ca 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1291,21 +1291,21 @@ iexit| SELECT ih.name, FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); -key_dependent_view| SELECT view_base_table.key, - view_base_table.data +key_dependent_view| SELECT key, + data FROM view_base_table - GROUP BY view_base_table.key; + GROUP BY key; key_dependent_view_no_cols| SELECT FROM view_base_table - GROUP BY view_base_table.key - HAVING (length((view_base_table.data)::text) > 0); -mvtest_tv| SELECT mvtest_t.type, - sum(mvtest_t.amt) AS totamt + GROUP BY key + HAVING (length((data)::text) > 0); +mvtest_tv| SELECT type, + sum(amt) AS totamt FROM mvtest_t - GROUP BY mvtest_t.type; -mvtest_tvv| SELECT sum(mvtest_tv.totamt) AS grandtot + GROUP BY type; +mvtest_tvv| SELECT sum(totamt) AS grandtot FROM mvtest_tv; -mvtest_tvvmv| SELECT mvtest_tvvm.grandtot +mvtest_tvvmv| SELECT grandtot FROM mvtest_tvvm; pg_available_extension_versions| SELECT e.name, e.version, @@ -1324,50 +1324,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1380,22 +1380,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1435,13 +1435,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1459,10 +1459,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1708,23 +1708,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1738,10 +1738,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1809,13 +1809,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1828,57 +1828,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1901,8 +1901,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1911,13 +1911,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2094,41 +2094,41 @@ pg_stat_subscription| SELECT su.oid AS subid, st.latest_end_time FROM (pg_subscription su LEFT JOIN pg_stat_get_subscription(NULL::oid) st(subid, relid, pid, received_lsn, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time) ON ((st.subid = su.oid))); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2138,68 +2138,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2216,19 +2216,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2238,19 +2238,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2289,64 +2289,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2529,24 +2529,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -2572,26 +2572,26 @@ pg_views| SELECT n.nspname AS schemaname, FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); -rtest_v1| SELECT rtest_t1.a, - rtest_t1.b +rtest_v1| SELECT a, + b FROM rtest_t1; rtest_vcomp| SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); -rtest_vview1| SELECT x.a, - x.b +rtest_vview1| SELECT a, + b FROM rtest_view1 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); -rtest_vview2| SELECT rtest_view1.a, - rtest_view1.b +rtest_vview2| SELECT a, + b FROM rtest_view1 - WHERE rtest_view1.v; -rtest_vview3| SELECT x.a, - x.b + WHERE v; +rtest_vview3| SELECT a, + b FROM rtest_vview2 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y @@ -2603,9 +2603,9 @@ rtest_vview4| SELECT x.a, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; -rtest_vview5| SELECT rtest_view1.a, - rtest_view1.b, - rtest_viewfunc1(rtest_view1.a) AS refcount +rtest_vview5| SELECT a, + b, + rtest_viewfunc1(a) AS refcount FROM rtest_view1; shoe| SELECT sh.shoename, sh.sh_avail, @@ -2635,20 +2635,20 @@ shoelace| SELECT s.sl_name, FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); -shoelace_candelete| SELECT shoelace_obsolete.sl_name, - shoelace_obsolete.sl_avail, - shoelace_obsolete.sl_color, - shoelace_obsolete.sl_len, - shoelace_obsolete.sl_unit, - shoelace_obsolete.sl_len_cm +shoelace_candelete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace_obsolete - WHERE (shoelace_obsolete.sl_avail = 0); -shoelace_obsolete| SELECT shoelace.sl_name, - shoelace.sl_avail, - shoelace.sl_color, - shoelace.sl_len, - shoelace.sl_unit, - shoelace.sl_len_cm + WHERE (sl_avail = 0); +shoelace_obsolete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace WHERE (NOT (EXISTS ( SELECT shoe.shoename FROM shoe @@ -2659,14 +2659,14 @@ street| SELECT r.name, FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); -test_tablesample_v1| SELECT test_tablesample.id +test_tablesample_v1| SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); -test_tablesample_v2| SELECT test_tablesample.id +test_tablesample_v2| SELECT id FROM test_tablesample TABLESAMPLE system (99); -toyemp| SELECT emp.name, - emp.age, - emp.location, - (12 * emp.salary) AS annualsal +toyemp| SELECT name, + age, + location, + (12 * salary) AS annualsal FROM emp; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') @@ -3256,7 +3256,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3295,8 +3295,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3308,8 +3308,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3321,8 +3321,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3334,8 +3334,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 078358d226..15a64aca0c 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 5d124cf96f..ed0f85e113 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1251,8 +1251,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index cdff914b93..a026946fb4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1666,19 +1666,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1719,17 +1719,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1759,17 +1759,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -1800,16 +1800,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -1831,15 +1831,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 3523a7dcc1..d262a7944e 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -802,9 +802,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1291,9 +1291,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1313,9 +1313,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-09-06 19:35  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Alvaro Herrera @ 2021-09-06 19:35 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Got this warning:

/pgsql/source/master/contrib/postgres_fdw/postgres_fdw.c: In function 'GetResultRelCheckAsUser':
/pgsql/source/master/contrib/postgres_fdw/postgres_fdw.c:1898:7: warning: unused variable 'result' [-Wunused-variable]
  Oid  result;
       ^~~~~~

I think the idea that GetRelPermissionInfo always has to scan the
complete list by OID is a nonstarter.  Maybe it would be possible to
store the list index of the PermissionInfo element in the RelOptInfo or
the RTE?  Maybe use special negative values if unknown (it knows to
search the first time) or known non-existant (probably a coding error
condition, maybe not necessary to have this)

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-09-10 03:22  Amit Langote <amitlangote09@gmail.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2021-09-10 03:22 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Thanks Alvaro for taking a look at this.

On Tue, Sep 7, 2021 at 4:35 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
> Got this warning:
>
> /pgsql/source/master/contrib/postgres_fdw/postgres_fdw.c: In function 'GetResultRelCheckAsUser':
> /pgsql/source/master/contrib/postgres_fdw/postgres_fdw.c:1898:7: warning: unused variable 'result' [-Wunused-variable]
>   Oid  result;
>        ^~~~~~

Fixed.

> I think the idea that GetRelPermissionInfo always has to scan the
> complete list by OID is a nonstarter.  Maybe it would be possible to
> store the list index of the PermissionInfo element in the RelOptInfo or
> the RTE?  Maybe use special negative values if unknown (it knows to
> search the first time) or known non-existant (probably a coding error
> condition, maybe not necessary to have this)

I implemented this by adding an Index field in RangeTblEntry, because
GetRelPermissionInfo() is used in all phases of query processing and
only RTEs exist from start to end.  I did have to spend some time
getting that approach right (get `make check` to pass!), especially to
ensure that the indexes remain in sync during the merging of
RelPermissionInfo across subqueries.  The comments I wrote around
GetRelPermissionInfo(), MergeRelPermissionInfos() functions should
hopefully make things clear.  Though, I do have a slightly uneasy
feeling around the fact that RTEs now store information that is
computed using some non-trivial logic, whereas most other fields are
simple catalog state or trivial details extracted from how the query
is spelled out by the user.

I also noticed that setrefs.c: add_rtes_to_flat_rtable() was still
doing things -- adding dead subquery RTEs and any RTEs referenced in
the underlying subquery to flat rtable -- that the new approach of
permissions handling makes unnecessary.  I fixed that oversight in the
updated patch.  A benefit from that simplification is that there is
now a single loop over rtable in that function rather than two that
were needed before.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v5-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 150.6K) download From 3410f01e1e3992531a12282a0f6c016984661df2 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v5 1/2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries that belong to inheritance child tables whose permissions need not be checked. This commit moves the permission checking information out of the range table entries into a new node type called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. Also, RTEs get a new Index field 'perminfoindex' to store the index in the aforementioned list of the RelPermissionInfo belonging to the RTE. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 81 ++++-- contrib/sepgsql/dml.c | 42 ++- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 ++++---- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 159 ++++++++---- src/backend/nodes/copyfuncs.c | 32 ++- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 ++- src/backend/nodes/readfuncs.c | 23 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 +++------ src/backend/optimizer/plan/subselect.c | 5 + src/backend/optimizer/prep/prepjointree.c | 10 + src/backend/optimizer/util/inherit.c | 170 +++++++++---- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 62 +++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 296 ++++++++++++++++------ src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 175 ++++++------- src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 87 ++++--- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 1082 insertions(+), 648 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 76d4fea21c..5c007d71ef 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -457,7 +459,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -622,7 +625,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -656,12 +658,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1514,16 +1516,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1803,7 +1804,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1882,6 +1884,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1893,6 +1924,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1900,6 +1932,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1923,6 +1956,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1934,7 +1968,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2126,6 +2161,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2203,6 +2239,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2219,7 +2257,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2605,7 +2644,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2625,13 +2663,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3929,12 +3966,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3946,12 +3983,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 1f96e8b507..44ec89840f 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 19a3ffb7ff..036a4c97f2 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 32405f8610..85221ada52 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 64f54393f3..f5624eeab9 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 790f4ccb92..ffc45efb32 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 53f4853141..522b800efe 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 40a54ad0bd..4e9e94eee0 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index c14ca27c5e..75e4b0cbf3 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dbee6ae199..7f71f5cfb3 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1172,7 +1172,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9494,7 +9495,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9636,7 +9638,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9822,7 +9825,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) table_close(pg_constraint, RowShareLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -9969,7 +9973,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -11878,7 +11883,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17561,7 +17567,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18447,7 +18454,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 4df05a0b33..5bfa730e8a 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b3ce4bae53..5bde876b78 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f8a4a40e7b..6c932b8261 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 5c723bc54e..f4456a1aca 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index ad11392b99..328aa6de53 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 83ec2a369e..6123a3480e 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -776,6 +777,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1264,6 +1266,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2455,6 +2476,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2480,12 +2502,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3170,6 +3186,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5131,6 +5148,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 4bad709f83..af2204b58a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -985,6 +985,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2736,6 +2737,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -2761,17 +2763,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3818,6 +3828,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 36e618611f..75e24a96fb 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -308,6 +308,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -702,6 +703,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -988,6 +990,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2263,6 +2280,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3073,6 +3091,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3252,6 +3271,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3305,12 +3325,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3993,6 +4007,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 0dd1ad7dfc..8f7dab5c24 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -266,6 +266,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1442,6 +1443,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1505,13 +1507,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1590,6 +1603,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2075,6 +2089,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2847,6 +2862,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RTE", 3)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index a5f6d678cc..700643d2c4 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4051,6 +4051,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5698,7 +5701,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 1e42d75465..be076d2d3c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5925,6 +5929,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6052,6 +6057,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index e50624c465..0e0bfbd07e 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -104,9 +104,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -258,7 +256,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -308,10 +306,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -324,29 +329,13 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. - */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -363,73 +352,27 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + glob->finalrelpermlist = + list_concat(glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally, add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); } /* @@ -438,9 +381,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -451,6 +392,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c9f7a09d10..13bfc8f84d 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1505,6 +1505,11 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subselect->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 224c5153b1..d75e4ccc5f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1269,6 +1274,11 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* + * Add the child query's RelPermissionInfos into the parent query. + */ + MergeRelPermissionInfos(root->parse, subquery->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index c758172efa..1e3177fd8a 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte, false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 47769cea45..bbfd5c0fca 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 146ee8dd1e..c3298e8625 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +859,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +875,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +903,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1062,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1355,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1590,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1837,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2299,6 +2309,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2365,6 +2376,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2383,7 +2395,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2395,7 +2407,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2436,8 +2448,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2724,6 +2736,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3202,9 +3215,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, + false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3260,9 +3281,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 078029ba1f..ad9717fffe 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3241,16 +3241,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index c5c3f26ecf..911de4ea7e 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,166 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless there already + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfo(*relpermlist, rte, true); + if (perminfo) + { + Assert(rte->perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = rte->relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Remember the index in the RTE. */ + Assert(rte->perminfoindex == 0); + rte->perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation in the provided + * list, erroring out or returning NULL (depending on missing_ok) if not + * found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * No need to scan the list on OID if the RTE contains a valid index, + * which is true in most cases except when MergeRelPermissionInfos() calls + * us (via AddRelPermissionInfo() that is). MergeRelPermissionInfos() + * intentionally resets the original index because it could potentially + * point into a different list than what we are passed, which is possible, + * for example, for RTEs that were just pulled up from another subquery. + * In that case, we force scanning the list by OID and reassign the index + * if an entry is found. + */ + if (rte->perminfoindex > 0) + { + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + + perminfo = (RelPermissionInfo *) list_nth(relpermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + + return perminfo; + } + else + { + ListCell *lc; + int i = 0; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == rte->relid) + { + /* And set the index in RTE. */ + rte->perminfoindex = i + 1; + return perminfo; + } + i++; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", rte->relid); + } + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source subquery given in + * src_relpermlist into dest_query, "merging" the contents of any that + * are present in both. + * + * This assumes that the caller has already pulled up the source subquery's + * RTEs into dest_query's rtable, because their perminfoindex would need to + * be updated to reflect their now belonging in the new mereged list. + */ +void +MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + ListCell *l1; + + foreach(l1, dest_query->rtable) + { + RangeTblEntry *dest_rte = (RangeTblEntry *) lfirst(l1); + RelPermissionInfo *dest_perminfo; + + /* + * Only RELATIONs have a RelPermissionInfo. Also ignore any that + * don't match the RelPermissionInfo we're trying to merge. + */ + if (dest_rte->rtekind != RTE_RELATION || + dest_rte->relid != src_perminfo->relid) + continue; + + /* + * Reset the index to signal to GetRelPermissionInfo() to re- + * assign the index by looking up an existing entry for the OID in + * dest_query->relpermlist. + */ + dest_rte->perminfoindex = 0; + dest_perminfo = AddRelPermissionInfo(&dest_query->relpermlist, + dest_rte); + /* "merge" proprties. */ + dest_perminfo->inh = src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 6e8fbc4780..6d308b2cd8 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1142,7 +1142,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 1d3ee53244..58b7fa8d2e 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 8d96c926b4..3f047f0289 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -463,6 +464,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1665,7 +1668,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1708,7 +1711,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1718,14 +1721,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6589345ac4..5ffcb58306 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 9521e81100..9c8062cd42 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,23 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(sub_action, parsetree->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1590,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1619,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1703,8 +1706,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1745,18 +1747,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1843,28 +1833,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1893,8 +1864,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3035,6 +3010,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3242,51 +3222,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte, + false); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3390,7 +3372,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3401,8 +3383,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3676,6 +3656,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3687,6 +3668,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry, + false); /* * We can use NoLock here since either the parser or @@ -3772,7 +3755,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index e10f94904e..f9d0691925 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 2e55913bc8..f438b71229 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1555,6 +1556,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1602,10 +1604,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 96269fc2ad..486fedfd43 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1308,8 +1308,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1327,32 +1327,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1362,9 +1356,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 0c8c05f6c2..15bb356572 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5135,7 +5135,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5187,7 +5188,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5271,7 +5273,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5319,7 +5322,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5380,6 +5384,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5388,7 +5393,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5457,7 +5463,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 778fa27fd1..f3ce859395 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 4d68d9cceb..752e204ec7 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 3dc03c913e..19ed90b956 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 37cb4f3d59..5d44c88238 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -522,6 +522,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -563,6 +571,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index a692eb7b09..70dc0f0888 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 45e4f2a16e..c85d793bdf 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -946,37 +948,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1030,11 +1001,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1154,14 +1131,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1abe233db2..244b5c5792 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -726,7 +728,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index ec9a8b0c81..7b39ca264a 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -636,6 +640,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index e9472f2f73..1ec96d89bd 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 9a15de5025..5b884ad96f 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index ee179082ce..e840ebf2bb 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 8336c2c5a2..15b4c9b03c 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte); +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 728a60c0b0..26300cc143 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
v5-0002-Do-not-add-OLD-NEW-RTEs-to-stored-view-rule-actio.patch (application/octet-stream, 114.8K) download From 63e35b63799e266a5883f8f0e6da961ebafd1328 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v5 2/2] Do not add OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes those RTEs unnecessary for that purpose. Also, this commit teaches the rewriter to add an RTE for any view relations referenced in a query so that they are locked during execution. The same RTE also ensures that the view relation OIDs are correctly reported into PlannedStmt.relationOids. As this changes the shape of the view queries stored in the catalog, a bunch of regression tests that display those queries now display them slightly differently, so their outputs have been updated. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 31 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 12 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 778 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- 22 files changed, 735 insertions(+), 802 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index e3ee30f1aa..5ed3c155cd 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2478,7 +2478,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2541,7 +2541,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6346,10 +6346,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6437,10 +6437,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 5bfa730e8a..e596683f27 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -323,78 +323,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -557,12 +485,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 9c8062cd42..8482fb54c3 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1706,7 +1706,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1824,17 +1825,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index e1b7e31458..aefcbab98a 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2080,7 +2080,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2096,7 +2096,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2112,7 +2112,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2128,7 +2128,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2146,7 +2146,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -2951,7 +2951,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 5949996ebc..57df8f7889 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1535,7 +1535,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1587,7 +1587,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1603,7 +1603,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1619,7 +1619,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2104,15 +2104,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 4bee0c1173..cfa69aab78 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2414,8 +2414,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2426,8 +2426,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2453,8 +2453,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2466,8 +2466,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 70133df804..4748db0305 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f06ae543e4..5f4c5c0db6 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index f50ef76685..dee7928ba2 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -341,10 +341,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -386,9 +386,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -402,9 +402,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -418,9 +418,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -434,9 +434,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -451,9 +451,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -467,9 +467,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -483,9 +483,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -499,9 +499,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -516,9 +516,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -532,9 +532,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -548,9 +548,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -564,9 +564,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -581,9 +581,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -597,9 +597,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -613,9 +613,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -629,9 +629,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -647,9 +647,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -663,9 +663,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -679,9 +679,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -695,9 +695,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -714,9 +714,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -730,9 +730,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -746,9 +746,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -762,9 +762,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1251,10 +1251,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1531,9 +1531,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1550,9 +1550,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1573,9 +1573,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1665,8 +1665,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1895,7 +1895,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -1947,19 +1947,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 7b6b0bb4f9..2b85967c1f 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -179,12 +179,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 4c467c1b15..71d6ec7034 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index b75afcc01a..493ad70db8 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -633,10 +633,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -648,10 +648,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -666,10 +666,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -680,10 +680,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index cafca1f9ae..6347c62774 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 2fa00a3c29..fefad636ca 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1291,21 +1291,21 @@ iexit| SELECT ih.name, FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); -key_dependent_view| SELECT view_base_table.key, - view_base_table.data +key_dependent_view| SELECT key, + data FROM view_base_table - GROUP BY view_base_table.key; + GROUP BY key; key_dependent_view_no_cols| SELECT FROM view_base_table - GROUP BY view_base_table.key - HAVING (length((view_base_table.data)::text) > 0); -mvtest_tv| SELECT mvtest_t.type, - sum(mvtest_t.amt) AS totamt + GROUP BY key + HAVING (length((data)::text) > 0); +mvtest_tv| SELECT type, + sum(amt) AS totamt FROM mvtest_t - GROUP BY mvtest_t.type; -mvtest_tvv| SELECT sum(mvtest_tv.totamt) AS grandtot + GROUP BY type; +mvtest_tvv| SELECT sum(totamt) AS grandtot FROM mvtest_tv; -mvtest_tvvmv| SELECT mvtest_tvvm.grandtot +mvtest_tvvmv| SELECT grandtot FROM mvtest_tvvm; pg_available_extension_versions| SELECT e.name, e.version, @@ -1324,50 +1324,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1380,22 +1380,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1435,13 +1435,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1459,10 +1459,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1708,23 +1708,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1738,10 +1738,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1809,13 +1809,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1828,57 +1828,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1901,8 +1901,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1911,13 +1911,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2094,41 +2094,41 @@ pg_stat_subscription| SELECT su.oid AS subid, st.latest_end_time FROM (pg_subscription su LEFT JOIN pg_stat_get_subscription(NULL::oid) st(subid, relid, pid, received_lsn, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time) ON ((st.subid = su.oid))); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2138,68 +2138,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2216,19 +2216,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2238,19 +2238,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2289,64 +2289,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2529,24 +2529,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -2572,26 +2572,26 @@ pg_views| SELECT n.nspname AS schemaname, FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); -rtest_v1| SELECT rtest_t1.a, - rtest_t1.b +rtest_v1| SELECT a, + b FROM rtest_t1; rtest_vcomp| SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); -rtest_vview1| SELECT x.a, - x.b +rtest_vview1| SELECT a, + b FROM rtest_view1 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); -rtest_vview2| SELECT rtest_view1.a, - rtest_view1.b +rtest_vview2| SELECT a, + b FROM rtest_view1 - WHERE rtest_view1.v; -rtest_vview3| SELECT x.a, - x.b + WHERE v; +rtest_vview3| SELECT a, + b FROM rtest_vview2 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y @@ -2603,9 +2603,9 @@ rtest_vview4| SELECT x.a, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; -rtest_vview5| SELECT rtest_view1.a, - rtest_view1.b, - rtest_viewfunc1(rtest_view1.a) AS refcount +rtest_vview5| SELECT a, + b, + rtest_viewfunc1(a) AS refcount FROM rtest_view1; shoe| SELECT sh.shoename, sh.sh_avail, @@ -2635,20 +2635,20 @@ shoelace| SELECT s.sl_name, FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); -shoelace_candelete| SELECT shoelace_obsolete.sl_name, - shoelace_obsolete.sl_avail, - shoelace_obsolete.sl_color, - shoelace_obsolete.sl_len, - shoelace_obsolete.sl_unit, - shoelace_obsolete.sl_len_cm +shoelace_candelete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace_obsolete - WHERE (shoelace_obsolete.sl_avail = 0); -shoelace_obsolete| SELECT shoelace.sl_name, - shoelace.sl_avail, - shoelace.sl_color, - shoelace.sl_len, - shoelace.sl_unit, - shoelace.sl_len_cm + WHERE (sl_avail = 0); +shoelace_obsolete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace WHERE (NOT (EXISTS ( SELECT shoe.shoename FROM shoe @@ -2659,14 +2659,14 @@ street| SELECT r.name, FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); -test_tablesample_v1| SELECT test_tablesample.id +test_tablesample_v1| SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); -test_tablesample_v2| SELECT test_tablesample.id +test_tablesample_v2| SELECT id FROM test_tablesample TABLESAMPLE system (99); -toyemp| SELECT emp.name, - emp.age, - emp.location, - (12 * emp.salary) AS annualsal +toyemp| SELECT name, + age, + location, + (12 * salary) AS annualsal FROM emp; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') @@ -3256,7 +3256,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3295,8 +3295,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3308,8 +3308,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3321,8 +3321,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3334,8 +3334,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 078358d226..15a64aca0c 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 5d124cf96f..ed0f85e113 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1251,8 +1251,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index cdff914b93..a026946fb4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1666,19 +1666,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1719,17 +1719,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1759,17 +1759,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -1800,16 +1800,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -1831,15 +1831,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 622d5da7d2..b44e6a4d5b 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -802,9 +802,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1291,9 +1291,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1313,9 +1313,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2021-12-20 07:13  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2021-12-20 07:13 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Sep 10, 2021 at 12:22 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Tue, Sep 7, 2021 at 4:35 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
> > I think the idea that GetRelPermissionInfo always has to scan the
> > complete list by OID is a nonstarter.  Maybe it would be possible to
> > store the list index of the PermissionInfo element in the RelOptInfo or
> > the RTE?  Maybe use special negative values if unknown (it knows to
> > search the first time) or known non-existant (probably a coding error
> > condition, maybe not necessary to have this)
>
> I implemented this by adding an Index field in RangeTblEntry, because
> GetRelPermissionInfo() is used in all phases of query processing and
> only RTEs exist from start to end.  I did have to spend some time
> getting that approach right (get `make check` to pass!), especially to
> ensure that the indexes remain in sync during the merging of
> RelPermissionInfo across subqueries.  The comments I wrote around
> GetRelPermissionInfo(), MergeRelPermissionInfos() functions should
> hopefully make things clear.  Though, I do have a slightly uneasy
> feeling around the fact that RTEs now store information that is
> computed using some non-trivial logic, whereas most other fields are
> simple catalog state or trivial details extracted from how the query
> is spelled out by the user.
>
> I also noticed that setrefs.c: add_rtes_to_flat_rtable() was still
> doing things -- adding dead subquery RTEs and any RTEs referenced in
> the underlying subquery to flat rtable -- that the new approach of
> permissions handling makes unnecessary.  I fixed that oversight in the
> updated patch.  A benefit from that simplification is that there is
> now a single loop over rtable in that function rather than two that
> were needed before.

Patch 0002 needed a rebase, because a conflicting change to
expected/rules.out has since been committed.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v6-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-rul.patch (application/octet-stream, 115.6K) download From ffdb27d8c083bb5f948d0ab9442947edf8e2f8c5 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v6 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for locking views during execition, so this commit teaches the rewriter to add an RTE for any views mentioned in the query. The same RTE also ensures that the view relation OIDs are correctly reported into PlannedStmt.relationOids. As this changes the shape of the view queries stored in the catalog (hidden OLD/NEW RTEs no longer present), a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 31 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 778 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- 22 files changed, 741 insertions(+), 808 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 5196e4797a..98c0339d93 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6403,10 +6403,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6494,10 +6494,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 5bfa730e8a..e596683f27 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -323,78 +323,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -557,12 +485,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 9c8062cd42..8482fb54c3 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1706,7 +1706,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1824,17 +1825,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index fd01651d9c..264ed6d361 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2145,7 +2145,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2161,7 +2161,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2177,7 +2177,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2193,7 +2193,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2211,7 +2211,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3068,7 +3068,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index be5fa5727d..6cb6fa108c 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1535,7 +1535,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1587,7 +1587,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1603,7 +1603,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1619,7 +1619,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2104,15 +2104,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 24d1c7cd28..3992a4d6da 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2462,8 +2462,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2474,8 +2474,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2501,8 +2501,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2514,8 +2514,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 70133df804..4748db0305 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f06ae543e4..5f4c5c0db6 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index f50ef76685..dee7928ba2 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -341,10 +341,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -386,9 +386,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -402,9 +402,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -418,9 +418,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -434,9 +434,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -451,9 +451,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -467,9 +467,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -483,9 +483,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -499,9 +499,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -516,9 +516,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -532,9 +532,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -548,9 +548,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -564,9 +564,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -581,9 +581,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -597,9 +597,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -613,9 +613,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -629,9 +629,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -647,9 +647,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -663,9 +663,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -679,9 +679,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -695,9 +695,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -714,9 +714,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -730,9 +730,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -746,9 +746,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -762,9 +762,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1251,10 +1251,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1531,9 +1531,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1550,9 +1550,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1573,9 +1573,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1665,8 +1665,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1895,7 +1895,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -1947,19 +1947,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 6406fb3a76..46264d4185 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -179,12 +179,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -213,12 +213,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 4c467c1b15..71d6ec7034 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index b58b062b10..00a03ff04e 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1291,21 +1291,21 @@ iexit| SELECT ih.name, FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); -key_dependent_view| SELECT view_base_table.key, - view_base_table.data +key_dependent_view| SELECT key, + data FROM view_base_table - GROUP BY view_base_table.key; + GROUP BY key; key_dependent_view_no_cols| SELECT FROM view_base_table - GROUP BY view_base_table.key - HAVING (length((view_base_table.data)::text) > 0); -mvtest_tv| SELECT mvtest_t.type, - sum(mvtest_t.amt) AS totamt + GROUP BY key + HAVING (length((data)::text) > 0); +mvtest_tv| SELECT type, + sum(amt) AS totamt FROM mvtest_t - GROUP BY mvtest_t.type; -mvtest_tvv| SELECT sum(mvtest_tv.totamt) AS grandtot + GROUP BY type; +mvtest_tvv| SELECT sum(totamt) AS grandtot FROM mvtest_tv; -mvtest_tvvmv| SELECT mvtest_tvvm.grandtot +mvtest_tvvmv| SELECT grandtot FROM mvtest_tvvm; pg_available_extension_versions| SELECT e.name, e.version, @@ -1324,50 +1324,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1380,22 +1380,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1435,13 +1435,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1459,10 +1459,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1708,23 +1708,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1738,10 +1738,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1809,13 +1809,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1828,57 +1828,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1901,8 +1901,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1911,13 +1911,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2112,41 +2112,41 @@ pg_stat_subscription_workers| SELECT w.subid, FROM pg_subscription_rel) sr, (LATERAL pg_stat_get_subscription_worker(sr.subid, sr.relid) w(subid, subrelid, last_error_relid, last_error_command, last_error_xid, last_error_count, last_error_message, last_error_time) JOIN pg_subscription s ON ((w.subid = s.oid))); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2156,68 +2156,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2234,19 +2234,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2256,19 +2256,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2307,64 +2307,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2547,24 +2547,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -2590,26 +2590,26 @@ pg_views| SELECT n.nspname AS schemaname, FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); -rtest_v1| SELECT rtest_t1.a, - rtest_t1.b +rtest_v1| SELECT a, + b FROM rtest_t1; rtest_vcomp| SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); -rtest_vview1| SELECT x.a, - x.b +rtest_vview1| SELECT a, + b FROM rtest_view1 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); -rtest_vview2| SELECT rtest_view1.a, - rtest_view1.b +rtest_vview2| SELECT a, + b FROM rtest_view1 - WHERE rtest_view1.v; -rtest_vview3| SELECT x.a, - x.b + WHERE v; +rtest_vview3| SELECT a, + b FROM rtest_vview2 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y @@ -2621,9 +2621,9 @@ rtest_vview4| SELECT x.a, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; -rtest_vview5| SELECT rtest_view1.a, - rtest_view1.b, - rtest_viewfunc1(rtest_view1.a) AS refcount +rtest_vview5| SELECT a, + b, + rtest_viewfunc1(a) AS refcount FROM rtest_view1; shoe| SELECT sh.shoename, sh.sh_avail, @@ -2653,20 +2653,20 @@ shoelace| SELECT s.sl_name, FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); -shoelace_candelete| SELECT shoelace_obsolete.sl_name, - shoelace_obsolete.sl_avail, - shoelace_obsolete.sl_color, - shoelace_obsolete.sl_len, - shoelace_obsolete.sl_unit, - shoelace_obsolete.sl_len_cm +shoelace_candelete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace_obsolete - WHERE (shoelace_obsolete.sl_avail = 0); -shoelace_obsolete| SELECT shoelace.sl_name, - shoelace.sl_avail, - shoelace.sl_color, - shoelace.sl_len, - shoelace.sl_unit, - shoelace.sl_len_cm + WHERE (sl_avail = 0); +shoelace_obsolete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace WHERE (NOT (EXISTS ( SELECT shoe.shoename FROM shoe @@ -2677,14 +2677,14 @@ street| SELECT r.name, FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); -test_tablesample_v1| SELECT test_tablesample.id +test_tablesample_v1| SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); -test_tablesample_v2| SELECT test_tablesample.id +test_tablesample_v2| SELECT id FROM test_tablesample TABLESAMPLE system (99); -toyemp| SELECT emp.name, - emp.age, - emp.location, - (12 * emp.salary) AS annualsal +toyemp| SELECT name, + age, + location, + (12 * salary) AS annualsal FROM emp; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') @@ -3274,7 +3274,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3313,8 +3313,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3326,8 +3326,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3339,8 +3339,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3352,8 +3352,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 5d124cf96f..ed0f85e113 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1251,8 +1251,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index cdff914b93..a026946fb4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1666,19 +1666,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1719,17 +1719,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1759,17 +1759,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -1800,16 +1800,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -1831,15 +1831,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 75e61460d9..cb76789cb1 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) -- 2.24.1
v6-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 150.6K) download From a833208d6b0b4d9c87b95943db11adb9ccd71f95 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v6 1/2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries that belong to inheritance child tables whose permissions need not be checked. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. Also, RTEs get a new Index field 'perminfoindex' to store the index in the aforementioned list of the RelPermissionInfo belonging to the RTE. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 81 ++++-- contrib/sepgsql/dml.c | 42 ++- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 ++++---- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 159 ++++++++---- src/backend/nodes/copyfuncs.c | 32 ++- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 ++- src/backend/nodes/readfuncs.c | 23 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 +++------ src/backend/optimizer/plan/subselect.c | 5 + src/backend/optimizer/prep/prepjointree.c | 10 + src/backend/optimizer/util/inherit.c | 170 +++++++++---- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 62 +++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 296 ++++++++++++++++------ src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 175 ++++++------- src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 87 ++++--- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 1082 insertions(+), 648 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index fa9a099f13..0ef3b11f1d 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -458,7 +460,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -623,7 +626,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -657,12 +659,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1515,16 +1517,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1804,7 +1805,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1883,6 +1885,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1894,6 +1925,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1901,6 +1933,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1924,6 +1957,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1935,7 +1969,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2127,6 +2162,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2204,6 +2240,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2220,7 +2258,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2606,7 +2645,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2626,13 +2664,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3930,12 +3967,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3947,12 +3984,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 1f96e8b507..44ec89840f 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 19a3ffb7ff..036a4c97f2 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 32405f8610..85221ada52 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 64f54393f3..f5624eeab9 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 790f4ccb92..ffc45efb32 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 53f4853141..522b800efe 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index f366a818a1..81b037ccbb 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 8d3104821e..3bb7688242 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index bf42587e38..fa0d3a112b 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1175,7 +1175,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9601,7 +9602,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9749,7 +9751,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9939,7 +9942,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) table_close(pg_constraint, RowShareLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10092,7 +10096,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12009,7 +12014,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17740,7 +17746,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18645,7 +18652,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 4df05a0b33..5bfa730e8a 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b3ce4bae53..5bde876b78 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f8a4a40e7b..6c932b8261 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 5c723bc54e..f4456a1aca 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 4ab1302313..9d2113488c 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index df0b747883..154d452f39 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -780,6 +781,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1270,6 +1272,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2462,6 +2483,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2487,12 +2509,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3178,6 +3194,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5151,6 +5168,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index cb7ddd463c..bb3817ba7d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -992,6 +992,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2764,6 +2765,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -2789,17 +2791,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3838,6 +3848,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 91a89b6d51..79991a19c5 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -709,6 +710,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -997,6 +999,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2273,6 +2290,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3077,6 +3095,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3256,6 +3275,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3309,12 +3329,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3998,6 +4012,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 76cff8a2b1..59f49ef8b0 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -266,6 +266,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1442,6 +1443,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1505,13 +1507,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1590,6 +1603,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2075,6 +2089,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2849,6 +2864,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f12660a260..d86a585a03 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4056,6 +4056,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5704,7 +5707,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index bd01ec0526..ee49ac4b3e 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5926,6 +5930,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6053,6 +6058,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 6ccec759bd..54178305ce 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -104,9 +104,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -259,7 +257,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -345,10 +343,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -361,29 +366,13 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. - */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -400,73 +389,27 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + glob->finalrelpermlist = + list_concat(glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally, add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); } /* @@ -475,9 +418,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -488,6 +429,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c9f7a09d10..13bfc8f84d 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1505,6 +1505,11 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subselect->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 387a35e112..de70bcf498 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1269,6 +1274,11 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* + * Add the child query's RelPermissionInfos into the parent query. + */ + MergeRelPermissionInfos(root->parse, subquery->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index c758172efa..1e3177fd8a 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte, false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 47769cea45..bbfd5c0fca 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 146ee8dd1e..c3298e8625 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +859,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +875,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +903,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1062,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1355,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1590,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1837,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2299,6 +2309,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2365,6 +2376,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2383,7 +2395,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2395,7 +2407,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2436,8 +2448,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2724,6 +2736,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3202,9 +3215,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, + false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3260,9 +3281,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 078029ba1f..ad9717fffe 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3241,16 +3241,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index c5c3f26ecf..911de4ea7e 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,166 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless there already + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfo(*relpermlist, rte, true); + if (perminfo) + { + Assert(rte->perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = rte->relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Remember the index in the RTE. */ + Assert(rte->perminfoindex == 0); + rte->perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation in the provided + * list, erroring out or returning NULL (depending on missing_ok) if not + * found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * No need to scan the list on OID if the RTE contains a valid index, + * which is true in most cases except when MergeRelPermissionInfos() calls + * us (via AddRelPermissionInfo() that is). MergeRelPermissionInfos() + * intentionally resets the original index because it could potentially + * point into a different list than what we are passed, which is possible, + * for example, for RTEs that were just pulled up from another subquery. + * In that case, we force scanning the list by OID and reassign the index + * if an entry is found. + */ + if (rte->perminfoindex > 0) + { + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + + perminfo = (RelPermissionInfo *) list_nth(relpermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + + return perminfo; + } + else + { + ListCell *lc; + int i = 0; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == rte->relid) + { + /* And set the index in RTE. */ + rte->perminfoindex = i + 1; + return perminfo; + } + i++; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", rte->relid); + } + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source subquery given in + * src_relpermlist into dest_query, "merging" the contents of any that + * are present in both. + * + * This assumes that the caller has already pulled up the source subquery's + * RTEs into dest_query's rtable, because their perminfoindex would need to + * be updated to reflect their now belonging in the new mereged list. + */ +void +MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + ListCell *l1; + + foreach(l1, dest_query->rtable) + { + RangeTblEntry *dest_rte = (RangeTblEntry *) lfirst(l1); + RelPermissionInfo *dest_perminfo; + + /* + * Only RELATIONs have a RelPermissionInfo. Also ignore any that + * don't match the RelPermissionInfo we're trying to merge. + */ + if (dest_rte->rtekind != RTE_RELATION || + dest_rte->relid != src_perminfo->relid) + continue; + + /* + * Reset the index to signal to GetRelPermissionInfo() to re- + * assign the index by looking up an existing entry for the OID in + * dest_query->relpermlist. + */ + dest_rte->perminfoindex = 0; + dest_perminfo = AddRelPermissionInfo(&dest_query->relpermlist, + dest_rte); + /* "merge" proprties. */ + dest_perminfo->inh = src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 9ce3a0de96..fc58425693 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1375,6 +1375,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1389,7 +1390,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1421,12 +1425,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 2d857a301b..3bbf005e71 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 2e79302a48..6faafaca67 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -463,6 +464,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1668,7 +1671,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1711,7 +1714,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1721,14 +1724,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6589345ac4..5ffcb58306 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 9521e81100..9c8062cd42 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,23 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(sub_action, parsetree->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1590,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1619,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1703,8 +1706,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1745,18 +1747,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1843,28 +1833,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1893,8 +1864,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3035,6 +3010,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3242,51 +3222,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte, + false); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3390,7 +3372,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3401,8 +3383,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3676,6 +3656,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3687,6 +3668,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry, + false); /* * We can use NoLock here since either the parser or @@ -3772,7 +3755,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index e10f94904e..f9d0691925 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 69ca52094f..6bca5a74a2 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1559,6 +1560,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1606,10 +1608,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 8ebb2a50a1..348ffbbd55 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1361,8 +1361,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1380,32 +1380,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1415,9 +1409,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 10895fb287..708dd121f6 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5135,7 +5135,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5187,7 +5188,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5271,7 +5273,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5319,7 +5322,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5380,6 +5384,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5388,7 +5393,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5457,7 +5463,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 778fa27fd1..f3ce859395 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 4d68d9cceb..752e204ec7 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index cd57a704ad..43a28a30e1 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index ddc3529332..186a85a725 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -523,6 +523,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -564,6 +572,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 7c657c1241..b25437dd4c 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 4c5a8a39bf..4ec1237a23 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -946,37 +948,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1030,11 +1001,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1154,14 +1131,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 324d92880b..386f3f7f80 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -730,7 +732,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index be3c30704a..e64a636813 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -636,6 +640,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index e9472f2f73..1ec96d89bd 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 9a15de5025..5b884ad96f 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index ee179082ce..e840ebf2bb 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 8336c2c5a2..15b4c9b03c 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte); +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 728a60c0b0..26300cc143 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-01-13 03:10  Julien Rouhaud <rjuju123@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Julien Rouhaud @ 2022-01-13 03:10 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,

On Mon, Dec 20, 2021 at 04:13:04PM +0900, Amit Langote wrote:
> 
> Patch 0002 needed a rebase, because a conflicting change to
> expected/rules.out has since been committed.

The cfbot reports new conflicts since about a week ago with this patch:

Latest failure: https://cirrus-ci.com/task/4686414276198400 and
https://api.cirrus-ci.com/v1/artifact/task/4686414276198400/regress_diffs/src/test/regress/regressio...

inline patch 1: /tmp/cirrus-ci-build/src/test/regress/results/xml.out, /tmp/cirrus-ci-build/src/test/regress/results/compression.out (+5/-5 lines) download inline-1.patchdiff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/xml.out /tmp/cirrus-ci-build/src/test/regress/results/xml.out --- /tmp/cirrus-ci-build/src/test/regress/expected/xml.out 2022-01-12 05:24:02.795477001 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/xml.out 2022-01-12 05:28:20.329086031 +0000 @@ -603,12 +603,12 @@ CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/compression.out /tmp/cirrus-ci-build/src/test/regress/results/compression.out --- /tmp/cirrus-ci-build/src/test/regress/expected/compression.out 2022-01-12 05:24:02.739471690 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/compression.out 2022-01-12 05:28:23.537403929 +0000 @@ -187,7 +187,7 @@ --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1;
Could you send a rebased patch? In the meantime I'll switch the cf entry to Waiting on Author. ^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-01-13 06:39  Amit Langote <amitlangote09@gmail.com>
  parent: Julien Rouhaud <rjuju123@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-01-13 06:39 UTC (permalink / raw)
  To: Julien Rouhaud <rjuju123@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Jan 13, 2022 at 12:10 PM Julien Rouhaud <rjuju123@gmail.com> wrote:
> On Mon, Dec 20, 2021 at 04:13:04PM +0900, Amit Langote wrote:
> >
> > Patch 0002 needed a rebase, because a conflicting change to
> > expected/rules.out has since been committed.
>
> The cfbot reports new conflicts since about a week ago with this patch:

I had noticed that too and was meaning to send a new version.  Thanks
for the reminder.

> Latest failure: https://cirrus-ci.com/task/4686414276198400 and
> https://api.cirrus-ci.com/v1/artifact/task/4686414276198400/regress_diffs/src/test/regress/regressio...
>
> diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/xml.out /tmp/cirrus-ci-build/src/test/regress/results/xml.out
> --- /tmp/cirrus-ci-build/src/test/regress/expected/xml.out      2022-01-12 05:24:02.795477001 +0000
> +++ /tmp/cirrus-ci-build/src/test/regress/results/xml.out       2022-01-12 05:28:20.329086031 +0000
> @@ -603,12 +603,12 @@
>  CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text);
>  SELECT table_name, view_definition FROM information_schema.views
>    WHERE table_name LIKE 'xmlview%' ORDER BY 1;
> - table_name |                                                  view_definition
> -------------+-------------------------------------------------------------------------------------------------------------------
> + table_name |                                              view_definition
> +------------+------------------------------------------------------------------------------------------------------------
>   xmlview1   |  SELECT xmlcomment('test'::text) AS xmlcomment;
>   xmlview2   |  SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat";
>   xmlview3   |  SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement";
> - xmlview4   |  SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+
> + xmlview4   |  SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement"     +
>              |    FROM emp;
>   xmlview5   |  SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse";
>   xmlview6   |  SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi";
> diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/compression.out /tmp/cirrus-ci-build/src/test/regress/results/compression.out
> --- /tmp/cirrus-ci-build/src/test/regress/expected/compression.out      2022-01-12 05:24:02.739471690 +0000
> +++ /tmp/cirrus-ci-build/src/test/regress/results/compression.out       2022-01-12 05:28:23.537403929 +0000
> @@ -187,7 +187,7 @@
>  --------+------+-----------+----------+---------+----------+-------------+--------------+-------------
>   x      | text |           |          |         | extended |             |              |
>  View definition:
> - SELECT cmdata1.f1 AS x
> + SELECT f1 AS x
>     FROM cmdata1;
>
>  SELECT pg_column_compression(f1) FROM cmdata1;
> @@ -274,7 +274,7 @@
>  --------+------+-----------+----------+---------+----------+-------------+--------------+-------------
>   x      | text |           |          |         | extended | lz4         |              |
>  View definition:
> - SELECT cmdata1.f1 AS x
> + SELECT f1 AS x
>     FROM cmdata1;
>
> Could you send a rebased patch?  In the meantime I'll switch the cf entry to
> Waiting on Author.

Turns out I had never compiled this patch set to exercise xml and lz4
tests, whose output files contained view definitions shown using \d
that also needed to be updated in the 0002 patch.

Fixed in the attached updated version.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v7-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-rul.patch (application/octet-stream, 120.1K) download From 704b08725e442b85ef3668c646fa3c8590f2beb6 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v7 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for locking views during execition, so this commit teaches the rewriter to add an RTE for any views mentioned in the query. The same RTE also ensures that the view relation OIDs are correctly reported into PlannedStmt.relationOids. As this changes the shape of the view queries stored in the catalog (hidden OLD/NEW RTEs no longer present), a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 31 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 778 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 25 files changed, 749 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 7d6f7d9e3d..d640db20dd 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6403,10 +6403,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6497,10 +6497,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index d63ea40b7f..1eade4e6ab 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -323,78 +323,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -557,12 +485,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 96d2319e0d..a41c1c7462 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1706,7 +1706,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1824,17 +1825,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 4200bcb0d7..1e0cb87fb4 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2145,7 +2145,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2161,7 +2161,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2177,7 +2177,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2193,7 +2193,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2211,7 +2211,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3068,7 +3068,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index be5fa5727d..6cb6fa108c 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1535,7 +1535,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1587,7 +1587,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1603,7 +1603,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1619,7 +1619,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2104,15 +2104,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 16e0475663..5cf4b31abd 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 70133df804..4748db0305 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f06ae543e4..5f4c5c0db6 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index f50ef76685..dee7928ba2 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -341,10 +341,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -386,9 +386,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -402,9 +402,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -418,9 +418,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -434,9 +434,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -451,9 +451,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -467,9 +467,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -483,9 +483,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -499,9 +499,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -516,9 +516,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -532,9 +532,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -548,9 +548,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -564,9 +564,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -581,9 +581,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -597,9 +597,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -613,9 +613,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -629,9 +629,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -647,9 +647,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -663,9 +663,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -679,9 +679,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -695,9 +695,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -714,9 +714,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -730,9 +730,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -746,9 +746,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -762,9 +762,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1251,10 +1251,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1531,9 +1531,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1550,9 +1550,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1573,9 +1573,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1665,8 +1665,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1895,7 +1895,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -1947,19 +1947,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 6406fb3a76..46264d4185 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -179,12 +179,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -213,12 +213,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 4c467c1b15..71d6ec7034 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index b58b062b10..00a03ff04e 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1291,21 +1291,21 @@ iexit| SELECT ih.name, FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); -key_dependent_view| SELECT view_base_table.key, - view_base_table.data +key_dependent_view| SELECT key, + data FROM view_base_table - GROUP BY view_base_table.key; + GROUP BY key; key_dependent_view_no_cols| SELECT FROM view_base_table - GROUP BY view_base_table.key - HAVING (length((view_base_table.data)::text) > 0); -mvtest_tv| SELECT mvtest_t.type, - sum(mvtest_t.amt) AS totamt + GROUP BY key + HAVING (length((data)::text) > 0); +mvtest_tv| SELECT type, + sum(amt) AS totamt FROM mvtest_t - GROUP BY mvtest_t.type; -mvtest_tvv| SELECT sum(mvtest_tv.totamt) AS grandtot + GROUP BY type; +mvtest_tvv| SELECT sum(totamt) AS grandtot FROM mvtest_tv; -mvtest_tvvmv| SELECT mvtest_tvvm.grandtot +mvtest_tvvmv| SELECT grandtot FROM mvtest_tvvm; pg_available_extension_versions| SELECT e.name, e.version, @@ -1324,50 +1324,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1380,22 +1380,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1435,13 +1435,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1459,10 +1459,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1708,23 +1708,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1738,10 +1738,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1809,13 +1809,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1828,57 +1828,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1901,8 +1901,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1911,13 +1911,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2112,41 +2112,41 @@ pg_stat_subscription_workers| SELECT w.subid, FROM pg_subscription_rel) sr, (LATERAL pg_stat_get_subscription_worker(sr.subid, sr.relid) w(subid, subrelid, last_error_relid, last_error_command, last_error_xid, last_error_count, last_error_message, last_error_time) JOIN pg_subscription s ON ((w.subid = s.oid))); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2156,68 +2156,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2234,19 +2234,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2256,19 +2256,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2307,64 +2307,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2547,24 +2547,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -2590,26 +2590,26 @@ pg_views| SELECT n.nspname AS schemaname, FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); -rtest_v1| SELECT rtest_t1.a, - rtest_t1.b +rtest_v1| SELECT a, + b FROM rtest_t1; rtest_vcomp| SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); -rtest_vview1| SELECT x.a, - x.b +rtest_vview1| SELECT a, + b FROM rtest_view1 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); -rtest_vview2| SELECT rtest_view1.a, - rtest_view1.b +rtest_vview2| SELECT a, + b FROM rtest_view1 - WHERE rtest_view1.v; -rtest_vview3| SELECT x.a, - x.b + WHERE v; +rtest_vview3| SELECT a, + b FROM rtest_vview2 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y @@ -2621,9 +2621,9 @@ rtest_vview4| SELECT x.a, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; -rtest_vview5| SELECT rtest_view1.a, - rtest_view1.b, - rtest_viewfunc1(rtest_view1.a) AS refcount +rtest_vview5| SELECT a, + b, + rtest_viewfunc1(a) AS refcount FROM rtest_view1; shoe| SELECT sh.shoename, sh.sh_avail, @@ -2653,20 +2653,20 @@ shoelace| SELECT s.sl_name, FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); -shoelace_candelete| SELECT shoelace_obsolete.sl_name, - shoelace_obsolete.sl_avail, - shoelace_obsolete.sl_color, - shoelace_obsolete.sl_len, - shoelace_obsolete.sl_unit, - shoelace_obsolete.sl_len_cm +shoelace_candelete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace_obsolete - WHERE (shoelace_obsolete.sl_avail = 0); -shoelace_obsolete| SELECT shoelace.sl_name, - shoelace.sl_avail, - shoelace.sl_color, - shoelace.sl_len, - shoelace.sl_unit, - shoelace.sl_len_cm + WHERE (sl_avail = 0); +shoelace_obsolete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace WHERE (NOT (EXISTS ( SELECT shoe.shoename FROM shoe @@ -2677,14 +2677,14 @@ street| SELECT r.name, FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); -test_tablesample_v1| SELECT test_tablesample.id +test_tablesample_v1| SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); -test_tablesample_v2| SELECT test_tablesample.id +test_tablesample_v2| SELECT id FROM test_tablesample TABLESAMPLE system (99); -toyemp| SELECT emp.name, - emp.age, - emp.location, - (12 * emp.salary) AS annualsal +toyemp| SELECT name, + age, + location, + (12 * salary) AS annualsal FROM emp; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') @@ -3274,7 +3274,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3313,8 +3313,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3326,8 +3326,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3339,8 +3339,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3352,8 +3352,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 5c0e7c2b79..888291d049 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1251,8 +1251,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index cdff914b93..a026946fb4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1666,19 +1666,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1719,17 +1719,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1759,17 +1759,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -1800,16 +1800,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -1831,15 +1831,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index f15ece3bd1..83dfd298f9 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55b65ef324..bb994a05de 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 0484260281..974b14f859 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.24.1
v7-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 150.6K) download From 6088447507ce536dd8f895d6d86aab6e625b4879 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v7 1/2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries that belong to inheritance child tables whose permissions need not be checked. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. Also, RTEs get a new Index field 'perminfoindex' to store the index in the aforementioned list of the RelPermissionInfo belonging to the RTE. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 81 ++++-- contrib/sepgsql/dml.c | 42 ++- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 ++++---- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 159 ++++++++---- src/backend/nodes/copyfuncs.c | 32 ++- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 ++- src/backend/nodes/readfuncs.c | 23 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 +++------ src/backend/optimizer/plan/subselect.c | 5 + src/backend/optimizer/prep/prepjointree.c | 10 + src/backend/optimizer/util/inherit.c | 170 +++++++++---- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 62 +++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 296 ++++++++++++++++------ src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 175 ++++++------- src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 87 ++++--- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 1082 insertions(+), 648 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 09a3f5e23c..13d3113e47 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -458,7 +460,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -623,7 +626,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -657,12 +659,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1515,16 +1517,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1804,7 +1805,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1883,6 +1885,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1894,6 +1925,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1901,6 +1933,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1924,6 +1957,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1935,7 +1969,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2127,6 +2162,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2204,6 +2240,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2220,7 +2258,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2606,7 +2645,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2626,13 +2664,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3930,12 +3967,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3947,12 +3984,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index d71c802106..994fa4f1c4 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index bb9c21bc6b..7942b805dc 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 0d6b34206a..1ce82884fe 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index e5cf1bde13..8587b2b8c7 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1f0654c2f5..eb890ab1b8 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1192,7 +1192,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9624,7 +9625,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9791,7 +9793,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9997,7 +10000,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10175,7 +10179,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12269,7 +12274,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17997,7 +18003,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18924,7 +18931,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index e183ab097c..d63ea40b7f 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 549d9eb696..6c0041f916 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 5dd8ab7db2..8b61aebe88 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 90ed1485d1..981d7d3111 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..732f833120 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 456d563f34..581f424dda 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -781,6 +782,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1271,6 +1273,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2463,6 +2484,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2488,12 +2510,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3179,6 +3195,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5152,6 +5169,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 53beef1488..565e2ea9bc 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -992,6 +992,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2764,6 +2765,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -2789,17 +2791,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3838,6 +3848,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index c0bf27d28b..f3ed25ef9a 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -710,6 +711,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -998,6 +1000,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2274,6 +2291,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3078,6 +3096,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3257,6 +3276,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3310,12 +3330,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3999,6 +4013,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 3f68f7c18d..f994cd9908 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1440,6 +1441,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1503,13 +1505,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1588,6 +1601,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2074,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2848,6 +2863,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index cd6d72c763..fd5a1fe545 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4077,6 +4077,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5727,7 +5730,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index bd09f85aea..705afea0d6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5926,6 +5930,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6053,6 +6058,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index e44ae971b4..0dcd8bdaa8 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -104,9 +104,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -259,7 +257,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -345,10 +343,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -361,29 +366,13 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. - */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -400,73 +389,27 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + glob->finalrelpermlist = + list_concat(glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally, add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); } /* @@ -475,9 +418,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -488,6 +429,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 8c9408d372..f2936e0bfc 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1505,6 +1505,11 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subselect->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 282589dec8..f214059fb6 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1269,6 +1274,11 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* + * Add the child query's RelPermissionInfos into the parent query. + */ + MergeRelPermissionInfos(root->parse, subquery->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 8c5dc65947..07d61c19e0 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte, false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..cd70026f3e 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6ac2e9ce23..f5adbcfeb3 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +859,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +875,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +903,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1062,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1355,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1590,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1837,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2299,6 +2309,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2365,6 +2376,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2383,7 +2395,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2395,7 +2407,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2436,8 +2448,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2724,6 +2736,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3202,9 +3215,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, + false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3260,9 +3281,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d8b14ba7cd..7c10f262e0 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3241,16 +3241,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index cb9e177b5e..d84e713d9c 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,166 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless there already + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfo(*relpermlist, rte, true); + if (perminfo) + { + Assert(rte->perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = rte->relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Remember the index in the RTE. */ + Assert(rte->perminfoindex == 0); + rte->perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation in the provided + * list, erroring out or returning NULL (depending on missing_ok) if not + * found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * No need to scan the list on OID if the RTE contains a valid index, + * which is true in most cases except when MergeRelPermissionInfos() calls + * us (via AddRelPermissionInfo() that is). MergeRelPermissionInfos() + * intentionally resets the original index because it could potentially + * point into a different list than what we are passed, which is possible, + * for example, for RTEs that were just pulled up from another subquery. + * In that case, we force scanning the list by OID and reassign the index + * if an entry is found. + */ + if (rte->perminfoindex > 0) + { + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + + perminfo = (RelPermissionInfo *) list_nth(relpermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + + return perminfo; + } + else + { + ListCell *lc; + int i = 0; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == rte->relid) + { + /* And set the index in RTE. */ + rte->perminfoindex = i + 1; + return perminfo; + } + i++; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", rte->relid); + } + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source subquery given in + * src_relpermlist into dest_query, "merging" the contents of any that + * are present in both. + * + * This assumes that the caller has already pulled up the source subquery's + * RTEs into dest_query's rtable, because their perminfoindex would need to + * be updated to reflect their now belonging in the new mereged list. + */ +void +MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + ListCell *l1; + + foreach(l1, dest_query->rtable) + { + RangeTblEntry *dest_rte = (RangeTblEntry *) lfirst(l1); + RelPermissionInfo *dest_perminfo; + + /* + * Only RELATIONs have a RelPermissionInfo. Also ignore any that + * don't match the RelPermissionInfo we're trying to merge. + */ + if (dest_rte->rtekind != RTE_RELATION || + dest_rte->relid != src_perminfo->relid) + continue; + + /* + * Reset the index to signal to GetRelPermissionInfo() to re- + * assign the index by looking up an existing entry for the OID in + * dest_query->relpermlist. + */ + dest_rte->perminfoindex = 0; + dest_perminfo = AddRelPermissionInfo(&dest_query->relpermlist, + dest_rte); + /* "merge" proprties. */ + dest_perminfo->inh = src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 059eeb9e94..6ab35e8a16 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1375,6 +1375,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1389,7 +1390,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1421,12 +1425,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 72a13407be..acaeda6c76 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index c9af775bc1..47315b5f20 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -465,6 +466,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1703,7 +1706,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1746,7 +1749,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1756,14 +1759,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..1a9e5c8524 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 3d82138cb3..96d2319e0d 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,23 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(sub_action, parsetree->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1590,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1619,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1703,8 +1706,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1745,18 +1747,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1843,28 +1833,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1893,8 +1864,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3035,6 +3010,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3242,51 +3222,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte, + false); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3390,7 +3372,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3401,8 +3383,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3676,6 +3656,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3687,6 +3668,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry, + false); /* * We can use NoLock here since either the parser or @@ -3772,7 +3755,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index f0a046d65a..03f3881997 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 4ab9aaa591..28fcde38ec 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1559,6 +1560,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1606,10 +1608,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index c95cd32402..e11f1b55d2 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1361,8 +1361,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1380,32 +1380,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1415,9 +1409,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index aff748d67b..bc41264d5b 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5135,7 +5135,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5187,7 +5188,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5271,7 +5273,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5319,7 +5322,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5380,6 +5384,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5388,7 +5393,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5457,7 +5463,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 344399f6a8..e408100594 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 4ea8735dd8..e8484d2c0b 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -527,6 +527,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -568,6 +576,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 28cf5aefca..9e246b55eb 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 413e7c85a1..5f0a8b2e5a 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -946,37 +948,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1030,11 +1001,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1154,14 +1131,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1f33fe13c1..3b521ec3bc 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -730,7 +732,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 0b518ce6b2..0abc993369 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -651,6 +655,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index eb96b2cfc1..6b612cc37e 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 8c859d0d0e..34f101077a 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 06dc27995b..6168512b99 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte); +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-01-17 11:50  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-01-17 11:50 UTC (permalink / raw)
  To: Julien Rouhaud <rjuju123@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Jan 13, 2022 at 3:39 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Thu, Jan 13, 2022 at 12:10 PM Julien Rouhaud <rjuju123@gmail.com> wrote:
> > On Mon, Dec 20, 2021 at 04:13:04PM +0900, Amit Langote wrote:
> > > Patch 0002 needed a rebase, because a conflicting change to
> > > expected/rules.out has since been committed.
> >
> > The cfbot reports new conflicts since about a week ago with this patch:
> > Could you send a rebased patch?  In the meantime I'll switch the cf entry to
> > Waiting on Author.
>
> Turns out I had never compiled this patch set to exercise xml and lz4
> tests, whose output files contained view definitions shown using \d
> that also needed to be updated in the 0002 patch.
>
> Fixed in the attached updated version.

cfbot tells me it found a conflict when applying v7 on the latest
HEAD.  Fixed in the attached v8.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v8-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 150.6K) download From 4b30e090a22ff5f3956762f56f1b34bd699a8a03 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v8 1/2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries that belong to inheritance child tables whose permissions need not be checked. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. Also, RTEs get a new Index field 'perminfoindex' to store the index in the aforementioned list of the RelPermissionInfo belonging to the RTE. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 81 ++++-- contrib/sepgsql/dml.c | 42 ++- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 ++++---- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 159 ++++++++---- src/backend/nodes/copyfuncs.c | 32 ++- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 ++- src/backend/nodes/readfuncs.c | 23 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 +++------ src/backend/optimizer/plan/subselect.c | 5 + src/backend/optimizer/prep/prepjointree.c | 10 + src/backend/optimizer/util/inherit.c | 170 +++++++++---- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 62 +++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 296 ++++++++++++++++------ src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 175 ++++++------- src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 87 ++++--- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 1082 insertions(+), 648 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 09a3f5e23c..13d3113e47 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -458,7 +460,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -623,7 +626,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -657,12 +659,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1515,16 +1517,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1804,7 +1805,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1883,6 +1885,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1894,6 +1925,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1901,6 +1933,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1924,6 +1957,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1935,7 +1969,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2127,6 +2162,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2204,6 +2240,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2220,7 +2258,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2606,7 +2645,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2626,13 +2664,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3930,12 +3967,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3947,12 +3984,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index d71c802106..994fa4f1c4 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index bb9c21bc6b..7942b805dc 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 0d6b34206a..1ce82884fe 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index e5cf1bde13..8587b2b8c7 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1f0654c2f5..eb890ab1b8 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1192,7 +1192,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9624,7 +9625,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9791,7 +9793,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9997,7 +10000,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10175,7 +10179,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12269,7 +12274,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17997,7 +18003,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18924,7 +18931,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index e183ab097c..d63ea40b7f 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 549d9eb696..6c0041f916 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 5dd8ab7db2..8b61aebe88 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 90ed1485d1..981d7d3111 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..732f833120 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b105c26381..e672198eee 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -781,6 +782,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1271,6 +1273,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2463,6 +2484,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2488,12 +2510,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3179,6 +3195,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5152,6 +5169,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index ae37ea9464..5f289459a2 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -992,6 +992,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2764,6 +2765,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -2789,17 +2791,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3838,6 +3848,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index d28cea1567..15ef316992 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -710,6 +711,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -998,6 +1000,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2274,6 +2291,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3078,6 +3096,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3257,6 +3276,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3310,12 +3330,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3999,6 +4013,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 3f68f7c18d..f994cd9908 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1440,6 +1441,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1503,13 +1505,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1588,6 +1601,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2074,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2848,6 +2863,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index cd6d72c763..fd5a1fe545 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4077,6 +4077,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5727,7 +5730,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index bd09f85aea..705afea0d6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5926,6 +5930,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6053,6 +6058,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index e44ae971b4..0dcd8bdaa8 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -104,9 +104,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -259,7 +257,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -345,10 +343,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -361,29 +366,13 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. - */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -400,73 +389,27 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + glob->finalrelpermlist = + list_concat(glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally, add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); } /* @@ -475,9 +418,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -488,6 +429,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 41bd1ae7d4..b29c702394 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1505,6 +1505,11 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subselect->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 282589dec8..f214059fb6 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1269,6 +1274,11 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* + * Add the child query's RelPermissionInfos into the parent query. + */ + MergeRelPermissionInfos(root->parse, subquery->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 8c5dc65947..07d61c19e0 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte, false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..cd70026f3e 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6ac2e9ce23..f5adbcfeb3 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +859,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +875,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +903,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1062,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1355,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1590,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1837,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2299,6 +2309,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2365,6 +2376,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2383,7 +2395,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2395,7 +2407,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2436,8 +2448,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2724,6 +2736,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3202,9 +3215,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, + false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3260,9 +3281,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d8b14ba7cd..7c10f262e0 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3241,16 +3241,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index cb9e177b5e..d84e713d9c 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,166 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless there already + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfo(*relpermlist, rte, true); + if (perminfo) + { + Assert(rte->perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = rte->relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Remember the index in the RTE. */ + Assert(rte->perminfoindex == 0); + rte->perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation in the provided + * list, erroring out or returning NULL (depending on missing_ok) if not + * found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * No need to scan the list on OID if the RTE contains a valid index, + * which is true in most cases except when MergeRelPermissionInfos() calls + * us (via AddRelPermissionInfo() that is). MergeRelPermissionInfos() + * intentionally resets the original index because it could potentially + * point into a different list than what we are passed, which is possible, + * for example, for RTEs that were just pulled up from another subquery. + * In that case, we force scanning the list by OID and reassign the index + * if an entry is found. + */ + if (rte->perminfoindex > 0) + { + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + + perminfo = (RelPermissionInfo *) list_nth(relpermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + + return perminfo; + } + else + { + ListCell *lc; + int i = 0; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == rte->relid) + { + /* And set the index in RTE. */ + rte->perminfoindex = i + 1; + return perminfo; + } + i++; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", rte->relid); + } + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source subquery given in + * src_relpermlist into dest_query, "merging" the contents of any that + * are present in both. + * + * This assumes that the caller has already pulled up the source subquery's + * RTEs into dest_query's rtable, because their perminfoindex would need to + * be updated to reflect their now belonging in the new mereged list. + */ +void +MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + ListCell *l1; + + foreach(l1, dest_query->rtable) + { + RangeTblEntry *dest_rte = (RangeTblEntry *) lfirst(l1); + RelPermissionInfo *dest_perminfo; + + /* + * Only RELATIONs have a RelPermissionInfo. Also ignore any that + * don't match the RelPermissionInfo we're trying to merge. + */ + if (dest_rte->rtekind != RTE_RELATION || + dest_rte->relid != src_perminfo->relid) + continue; + + /* + * Reset the index to signal to GetRelPermissionInfo() to re- + * assign the index by looking up an existing entry for the OID in + * dest_query->relpermlist. + */ + dest_rte->perminfoindex = 0; + dest_perminfo = AddRelPermissionInfo(&dest_query->relpermlist, + dest_rte); + /* "merge" proprties. */ + dest_perminfo->inh = src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 059eeb9e94..6ab35e8a16 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1375,6 +1375,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1389,7 +1390,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1421,12 +1425,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0eea214dd8..2685dabf50 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index c9af775bc1..47315b5f20 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -465,6 +466,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1703,7 +1706,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1746,7 +1749,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1756,14 +1759,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..1a9e5c8524 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 3d82138cb3..96d2319e0d 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,23 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(sub_action, parsetree->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1590,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1619,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1703,8 +1706,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1745,18 +1747,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1843,28 +1833,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1893,8 +1864,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3035,6 +3010,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3242,51 +3222,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte, + false); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3390,7 +3372,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3401,8 +3383,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3676,6 +3656,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3687,6 +3668,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry, + false); /* * We can use NoLock here since either the parser or @@ -3772,7 +3755,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index f0a046d65a..03f3881997 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 87fe82ed11..f41588d297 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index c95cd32402..e11f1b55d2 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1361,8 +1361,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1380,32 +1380,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1415,9 +1409,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 1fbb0b28c3..934e21eda4 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5137,7 +5137,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5189,7 +5190,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5268,7 +5270,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5316,7 +5319,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5377,6 +5381,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5385,7 +5390,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5454,7 +5460,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 344399f6a8..e408100594 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 4ea8735dd8..e8484d2c0b 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -527,6 +527,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -568,6 +576,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 28cf5aefca..9e246b55eb 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 413e7c85a1..5f0a8b2e5a 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -946,37 +948,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1030,11 +1001,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1154,14 +1131,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1f3845b3fe..4b8ce415a1 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -730,7 +732,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 0b518ce6b2..0abc993369 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -651,6 +655,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index eb96b2cfc1..6b612cc37e 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 8c859d0d0e..34f101077a 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 06dc27995b..6168512b99 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte); +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
v8-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-rul.patch (application/octet-stream, 120.1K) download From d9b6a44c76f716ba6338ad52bb75527c5f7eaaff Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v8 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for locking views during execition, so this commit teaches the rewriter to add an RTE for any views mentioned in the query. The same RTE also ensures that the view relation OIDs are correctly reported into PlannedStmt.relationOids. As this changes the shape of the view queries stored in the catalog (hidden OLD/NEW RTEs no longer present), a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 31 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 778 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 25 files changed, 749 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 7d6f7d9e3d..d640db20dd 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6403,10 +6403,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6497,10 +6497,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index d63ea40b7f..1eade4e6ab 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -323,78 +323,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -557,12 +485,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 96d2319e0d..a41c1c7462 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1706,7 +1706,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1824,17 +1825,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 4200bcb0d7..1e0cb87fb4 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2145,7 +2145,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2161,7 +2161,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2177,7 +2177,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2193,7 +2193,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2211,7 +2211,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3068,7 +3068,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index be5fa5727d..6cb6fa108c 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1535,7 +1535,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1587,7 +1587,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1603,7 +1603,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1619,7 +1619,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2104,15 +2104,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 16e0475663..5cf4b31abd 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 70133df804..4748db0305 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f06ae543e4..5f4c5c0db6 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 509e930fc7..18d017a2d3 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -341,10 +341,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -386,9 +386,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -402,9 +402,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -418,9 +418,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -434,9 +434,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -451,9 +451,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -467,9 +467,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -483,9 +483,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -499,9 +499,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -516,9 +516,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -532,9 +532,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -548,9 +548,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -564,9 +564,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -581,9 +581,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -597,9 +597,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -613,9 +613,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -629,9 +629,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -647,9 +647,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -663,9 +663,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -679,9 +679,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -695,9 +695,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -714,9 +714,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -730,9 +730,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -746,9 +746,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -762,9 +762,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1251,10 +1251,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1531,9 +1531,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1550,9 +1550,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1573,9 +1573,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1665,8 +1665,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1911,7 +1911,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -1963,19 +1963,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 6406fb3a76..46264d4185 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -179,12 +179,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -213,12 +213,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 4c467c1b15..71d6ec7034 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index d652f7b5fb..311ccb353f 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1291,21 +1291,21 @@ iexit| SELECT ih.name, FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); -key_dependent_view| SELECT view_base_table.key, - view_base_table.data +key_dependent_view| SELECT key, + data FROM view_base_table - GROUP BY view_base_table.key; + GROUP BY key; key_dependent_view_no_cols| SELECT FROM view_base_table - GROUP BY view_base_table.key - HAVING (length((view_base_table.data)::text) > 0); -mvtest_tv| SELECT mvtest_t.type, - sum(mvtest_t.amt) AS totamt + GROUP BY key + HAVING (length((data)::text) > 0); +mvtest_tv| SELECT type, + sum(amt) AS totamt FROM mvtest_t - GROUP BY mvtest_t.type; -mvtest_tvv| SELECT sum(mvtest_tv.totamt) AS grandtot + GROUP BY type; +mvtest_tvv| SELECT sum(totamt) AS grandtot FROM mvtest_tv; -mvtest_tvvmv| SELECT mvtest_tvvm.grandtot +mvtest_tvvmv| SELECT grandtot FROM mvtest_tvvm; pg_available_extension_versions| SELECT e.name, e.version, @@ -1324,50 +1324,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1380,22 +1380,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1435,13 +1435,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1459,10 +1459,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1708,23 +1708,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1738,10 +1738,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1809,13 +1809,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1828,57 +1828,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1901,8 +1901,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1911,13 +1911,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2112,41 +2112,41 @@ pg_stat_subscription_workers| SELECT w.subid, FROM pg_subscription_rel) sr, (LATERAL pg_stat_get_subscription_worker(sr.subid, sr.relid) w(subid, subrelid, last_error_relid, last_error_command, last_error_xid, last_error_count, last_error_message, last_error_time) JOIN pg_subscription s ON ((w.subid = s.oid))); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2156,68 +2156,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2234,19 +2234,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2256,19 +2256,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2307,64 +2307,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2549,24 +2549,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -2592,26 +2592,26 @@ pg_views| SELECT n.nspname AS schemaname, FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); -rtest_v1| SELECT rtest_t1.a, - rtest_t1.b +rtest_v1| SELECT a, + b FROM rtest_t1; rtest_vcomp| SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); -rtest_vview1| SELECT x.a, - x.b +rtest_vview1| SELECT a, + b FROM rtest_view1 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); -rtest_vview2| SELECT rtest_view1.a, - rtest_view1.b +rtest_vview2| SELECT a, + b FROM rtest_view1 - WHERE rtest_view1.v; -rtest_vview3| SELECT x.a, - x.b + WHERE v; +rtest_vview3| SELECT a, + b FROM rtest_vview2 x WHERE (0 < ( SELECT count(*) AS count FROM rtest_view2 y @@ -2623,9 +2623,9 @@ rtest_vview4| SELECT x.a, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; -rtest_vview5| SELECT rtest_view1.a, - rtest_view1.b, - rtest_viewfunc1(rtest_view1.a) AS refcount +rtest_vview5| SELECT a, + b, + rtest_viewfunc1(a) AS refcount FROM rtest_view1; shoe| SELECT sh.shoename, sh.sh_avail, @@ -2655,20 +2655,20 @@ shoelace| SELECT s.sl_name, FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); -shoelace_candelete| SELECT shoelace_obsolete.sl_name, - shoelace_obsolete.sl_avail, - shoelace_obsolete.sl_color, - shoelace_obsolete.sl_len, - shoelace_obsolete.sl_unit, - shoelace_obsolete.sl_len_cm +shoelace_candelete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace_obsolete - WHERE (shoelace_obsolete.sl_avail = 0); -shoelace_obsolete| SELECT shoelace.sl_name, - shoelace.sl_avail, - shoelace.sl_color, - shoelace.sl_len, - shoelace.sl_unit, - shoelace.sl_len_cm + WHERE (sl_avail = 0); +shoelace_obsolete| SELECT sl_name, + sl_avail, + sl_color, + sl_len, + sl_unit, + sl_len_cm FROM shoelace WHERE (NOT (EXISTS ( SELECT shoe.shoename FROM shoe @@ -2679,14 +2679,14 @@ street| SELECT r.name, FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); -test_tablesample_v1| SELECT test_tablesample.id +test_tablesample_v1| SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); -test_tablesample_v2| SELECT test_tablesample.id +test_tablesample_v2| SELECT id FROM test_tablesample TABLESAMPLE system (99); -toyemp| SELECT emp.name, - emp.age, - emp.location, - (12 * emp.salary) AS annualsal +toyemp| SELECT name, + age, + location, + (12 * salary) AS annualsal FROM emp; SELECT tablename, rulename, definition FROM pg_rules WHERE schemaname IN ('pg_catalog', 'public') @@ -3276,7 +3276,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3315,8 +3315,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3328,8 +3328,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3341,8 +3341,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3354,8 +3354,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 5c0e7c2b79..888291d049 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1251,8 +1251,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index cdff914b93..a026946fb4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1666,19 +1666,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1719,17 +1719,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1759,17 +1759,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -1800,16 +1800,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -1831,15 +1831,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index f15ece3bd1..83dfd298f9 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55b65ef324..bb994a05de 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 0484260281..974b14f859 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-01-17 15:45  Zhihong Yu <zyu@yugabyte.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Zhihong Yu @ 2022-01-17 15:45 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Jan 17, 2022 at 3:51 AM Amit Langote <amitlangote09@gmail.com>
wrote:

> On Thu, Jan 13, 2022 at 3:39 PM Amit Langote <amitlangote09@gmail.com>
> wrote:
> > On Thu, Jan 13, 2022 at 12:10 PM Julien Rouhaud <rjuju123@gmail.com>
> wrote:
> > > On Mon, Dec 20, 2021 at 04:13:04PM +0900, Amit Langote wrote:
> > > > Patch 0002 needed a rebase, because a conflicting change to
> > > > expected/rules.out has since been committed.
> > >
> > > The cfbot reports new conflicts since about a week ago with this patch:
> > > Could you send a rebased patch?  In the meantime I'll switch the cf
> entry to
> > > Waiting on Author.
> >
> > Turns out I had never compiled this patch set to exercise xml and lz4
> > tests, whose output files contained view definitions shown using \d
> > that also needed to be updated in the 0002 patch.
> >
> > Fixed in the attached updated version.
>
> cfbot tells me it found a conflict when applying v7 on the latest
> HEAD.  Fixed in the attached v8.
>
> Hi,
For patch 02, in the description:

present for locking views during execition

Typo: execution.

+    * to be used by the executor to lock the view relation and for the
+    * planner to be able to record the view relation OID in the PlannedStmt
+    * that it produces for the query.

I think the sentence about executor can be placed after the sentence for
the planner.

For patch 01, GetRelPermissionInfo():

+       return perminfo;
+   }
+   else

keyword 'else' is not needed - the else block can be left-indented.

Cheers

^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-03-14 07:36  Amit Langote <amitlangote09@gmail.com>
  parent: Zhihong Yu <zyu@yugabyte.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-03-14 07:36 UTC (permalink / raw)
  To: Zhihong Yu <zyu@yugabyte.com>; +Cc: Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Jan 18, 2022 at 12:42 AM Zhihong Yu <zyu@yugabyte.com> wrote:
> Hi,
> For patch 02, in the description:

Thanks for looking.

> present for locking views during execition
>
> Typo: execution.
>
> +    * to be used by the executor to lock the view relation and for the
> +    * planner to be able to record the view relation OID in the PlannedStmt
> +    * that it produces for the query.
>
> I think the sentence about executor can be placed after the sentence for the planner.

Fixed.

> For patch 01, GetRelPermissionInfo():
>
> +       return perminfo;
> +   }
> +   else
>
> keyword 'else' is not needed - the else block can be left-indented.

OK, done.

Also needed fixes when rebasing.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v9-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-rul.patch (application/octet-stream, 116.2K) download From 1f838aea2838ff0a469641cbd0b12d044ba6c4ec Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v9 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 31 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 696 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 25 files changed, 708 insertions(+), 775 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index f210f91188..005f30299d 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6403,10 +6403,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6497,10 +6497,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 160c709044..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 96d2319e0d..a41c1c7462 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1706,7 +1706,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1824,17 +1825,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 3e55ff26f8..a7f0b14b51 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2154,7 +2154,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2170,7 +2170,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2186,7 +2186,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2202,7 +2202,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2220,7 +2220,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3113,7 +3113,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 0a23a39aa2..81d87a2678 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1545,7 +1545,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1597,7 +1597,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1613,7 +1613,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1629,7 +1629,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2114,15 +2114,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index aabc564e2c..a2322ebeec 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 9699ca16cf..5bb7c019ac 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index ae7c04353c..72a70f9f2b 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -370,10 +370,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -415,9 +415,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -431,9 +431,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -447,9 +447,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -463,9 +463,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -480,9 +480,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -496,9 +496,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -512,9 +512,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -528,9 +528,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -545,9 +545,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -561,9 +561,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -577,9 +577,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -593,9 +593,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -610,9 +610,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -626,9 +626,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -642,9 +642,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -658,9 +658,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -676,9 +676,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -692,9 +692,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -708,9 +708,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -724,9 +724,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -743,9 +743,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -759,9 +759,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -775,9 +775,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -791,9 +791,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1280,10 +1280,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1560,9 +1560,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1579,9 +1579,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1602,9 +1602,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1694,8 +1694,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1940,7 +1940,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -1992,19 +1992,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 58a25b691a..3aed54feb1 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index ac468568a1..81f4972e83 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,50 +1302,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1358,22 +1358,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1413,13 +1413,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1437,10 +1437,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1686,23 +1686,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1716,10 +1716,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1787,13 +1787,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1806,57 +1806,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1879,8 +1879,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1889,13 +1889,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2041,26 +2041,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2079,41 +2079,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2123,68 +2123,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2201,19 +2201,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2223,19 +2223,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2274,64 +2274,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2516,24 +2516,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3053,7 +3053,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3092,8 +3092,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3105,8 +3105,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3118,8 +3118,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3131,8 +3131,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index d3e02ca63b..00ff5bbfe6 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index cdff914b93..a026946fb4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1666,19 +1666,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1719,17 +1719,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1759,17 +1759,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -1800,16 +1800,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -1831,15 +1831,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index ff76ad4d6e..f6f78ec52d 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55b65ef324..bb994a05de 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 0484260281..974b14f859 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.24.1
v9-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 151.1K) download From 3b199a6d7f100947005b41bb612ac0cdb9f4f1e2 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v9 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 ++++-- contrib/sepgsql/dml.c | 42 ++- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 ++++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 159 +++++++---- src/backend/nodes/copyfuncs.c | 32 ++- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 +- src/backend/nodes/readfuncs.c | 23 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 +++------ src/backend/optimizer/plan/subselect.c | 5 + src/backend/optimizer/prep/prepjointree.c | 10 + src/backend/optimizer/util/inherit.c | 170 ++++++++---- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 62 +++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 289 ++++++++++++++------ src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 175 ++++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 87 +++--- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 52 files changed, 1076 insertions(+), 649 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 56654844e8..a33191de40 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -458,7 +460,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -623,7 +626,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -657,12 +659,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1515,16 +1517,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1816,7 +1817,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1895,6 +1897,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1906,6 +1937,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1913,6 +1945,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1936,6 +1969,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1947,7 +1981,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2139,6 +2174,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2216,6 +2252,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2232,7 +2270,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2618,7 +2657,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2638,13 +2676,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3942,12 +3979,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3959,12 +3996,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 97e61b8043..cb9aeb175d 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 7da7105d44..44b86cdedf 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 7b3f5a84b8..3b60bd0cea 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1380,7 +1386,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index cd30f15eba..356a93f7c5 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1231,7 +1231,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dc5872f988..6c1a9150e4 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1193,7 +1193,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9625,7 +9626,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9792,7 +9794,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9998,7 +10001,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10176,7 +10180,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12270,7 +12275,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17998,7 +18004,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18925,7 +18932,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..160c709044 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 549d9eb696..6c0041f916 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -89,8 +89,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -695,15 +685,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -813,9 +800,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 5dd8ab7db2..8b61aebe88 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 90ed1485d1..981d7d3111 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..732f833120 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d4f8455a2b..48506a2f5e 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -781,6 +782,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1271,6 +1273,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2463,6 +2484,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2488,12 +2510,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3183,6 +3199,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5178,6 +5195,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index f1002afe7a..c499611316 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -992,6 +992,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2775,6 +2776,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -2800,17 +2802,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3863,6 +3873,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 6bdad462c7..655eb2d888 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -710,6 +711,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -998,6 +1000,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2274,6 +2291,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3079,6 +3097,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3258,6 +3277,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3311,12 +3331,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -4009,6 +4023,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 3f68f7c18d..f994cd9908 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1440,6 +1441,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1503,13 +1505,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1588,6 +1601,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2074,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2848,6 +2863,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index fa069a217c..3097d38d42 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4093,6 +4093,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5743,7 +5746,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index bd09f85aea..705afea0d6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5926,6 +5930,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6053,6 +6058,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index a7b11b7f03..bdf8b66e4d 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -104,9 +104,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -259,7 +257,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -345,10 +343,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -361,29 +366,13 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. - */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -400,73 +389,27 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + glob->finalrelpermlist = + list_concat(glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally, add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); } /* @@ -475,9 +418,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -488,6 +429,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 41bd1ae7d4..b29c702394 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1505,6 +1505,11 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subselect->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 282589dec8..f214059fb6 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1269,6 +1274,11 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* + * Add the child query's RelPermissionInfos into the parent query. + */ + MergeRelPermissionInfos(root->parse, subquery->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..4da2b213ed 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte, false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..cd70026f3e 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 61026753a3..c1f290e578 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -516,6 +516,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -548,7 +549,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -667,6 +668,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -892,7 +900,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -908,8 +916,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -936,6 +944,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1094,8 +1103,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1389,6 +1396,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1623,6 +1631,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1869,6 +1878,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2340,6 +2350,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2406,6 +2417,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2424,7 +2436,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2436,7 +2448,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2477,8 +2489,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2765,6 +2777,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3243,9 +3256,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, + false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3301,9 +3322,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d8b14ba7cd..7c10f262e0 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3241,16 +3241,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index cb9e177b5e..f9ea9400c4 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,159 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless there already + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfo(*relpermlist, rte, true); + if (perminfo) + { + Assert(rte->perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = rte->relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Remember the index in the RTE. */ + Assert(rte->perminfoindex == 0); + rte->perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation in the provided + * list, erroring out or returning NULL (depending on missing_ok) if not + * found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok) +{ + RelPermissionInfo *perminfo; + ListCell *lc; + int i; + + Assert(rte->rtekind == RTE_RELATION); + + /* Easy when the RTE already knows. */ + if (rte->perminfoindex > 0) + { + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + + perminfo = (RelPermissionInfo *) list_nth(relpermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + + return perminfo; + } + + /* Looks like the RTE doesn't, so try to find it the hard way. */ + i = 0; + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == rte->relid) + { + /* And set the index in RTE. */ + rte->perminfoindex = i + 1; + return perminfo; + } + i++; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", rte->relid); + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source subquery given in + * src_relpermlist into dest_query, "merging" the contents of any that + * are present in both. + * + * This assumes that the caller has already pulled up the source subquery's + * RTEs into dest_query's rtable, because their perminfoindex would need to + * be updated to reflect their now belonging in the new mereged list. + */ +void +MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + ListCell *l1; + + foreach(l1, dest_query->rtable) + { + RangeTblEntry *dest_rte = (RangeTblEntry *) lfirst(l1); + RelPermissionInfo *dest_perminfo; + + /* + * Only RELATIONs have a RelPermissionInfo. Also ignore any that + * don't match the RelPermissionInfo we're trying to merge. + */ + if (dest_rte->rtekind != RTE_RELATION || + dest_rte->relid != src_perminfo->relid) + continue; + + /* + * The current value of perminfoindex points to a perminfo in the + * source query's relpermlist, which we're trying to merge with + * the destination query's relpermlist. So, reset the index to + * signal to GetRelPermissionInfo(), that gets called via + * AddRelPermissionInfo(), to assign it the index of an entry with + * the same relid in dest_query->relpermlist. + */ + dest_rte->perminfoindex = 0; + dest_perminfo = AddRelPermissionInfo(&dest_query->relpermlist, + dest_rte); + /* "merge" proprties. */ + dest_perminfo->inh = src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 059eeb9e94..6ab35e8a16 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1375,6 +1375,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1389,7 +1390,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1421,12 +1425,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index cd946c7692..e6117823dd 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3013,9 +3014,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3071,6 +3069,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3113,8 +3112,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 8653e1d840..1f92a32d64 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -467,6 +468,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1705,7 +1708,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1748,7 +1751,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1758,14 +1761,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index ea57a0477f..672167f49f 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -899,7 +899,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..1a9e5c8524 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 3d82138cb3..96d2319e0d 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,23 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(sub_action, parsetree->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1590,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1619,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1703,8 +1706,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1745,18 +1747,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1843,28 +1833,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1893,8 +1864,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3035,6 +3010,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3242,51 +3222,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte, + false); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3390,7 +3372,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3401,8 +3383,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3676,6 +3656,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3687,6 +3668,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry, + false); /* * We can use NoLock here since either the parser or @@ -3772,7 +3755,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index f0a046d65a..03f3881997 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ca48395d5c..cc68e68f1d 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index c95cd32402..e11f1b55d2 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1361,8 +1361,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1380,32 +1380,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1415,9 +1409,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 1fbb0b28c3..934e21eda4 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5137,7 +5137,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5189,7 +5190,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5268,7 +5270,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5316,7 +5319,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5377,6 +5381,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5385,7 +5390,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5454,7 +5460,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 344399f6a8..e408100594 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index dd95dc40c7..35c4cea8c3 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -528,6 +528,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -569,6 +577,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 5d075f0c34..a422955463 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 1617702d9d..fd9f698202 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -947,37 +949,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1031,11 +1002,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1155,14 +1132,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1f3845b3fe..4b8ce415a1 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -730,7 +732,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 0b518ce6b2..0abc993369 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -651,6 +655,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index eb96b2cfc1..6b612cc37e 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 8c859d0d0e..34f101077a 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 06dc27995b..6168512b99 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte); +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-03-23 07:03  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 3 replies; 97+ messages in thread

From: Amit Langote @ 2022-03-23 07:03 UTC (permalink / raw)
  To: Zhihong Yu <zyu@yugabyte.com>; +Cc: Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Mar 14, 2022 at 4:36 PM Amit Langote <amitlangote09@gmail.com> wrote:
> Also needed fixes when rebasing.

Needed another rebase.

As the changes being made with the patch are non-trivial and the patch
hasn't been reviewed very significantly since Alvaro's comments back
in Sept 2021 which I've since addressed, I'm thinking of pushing this
one into the version 16 dev cycle.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v10-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 116.2K) download From 8b1fe9b7c2aec0e072f24c2952c57b242c96b3c7 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v10 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 31 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 696 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 25 files changed, 708 insertions(+), 775 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index f210f91188..005f30299d 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6403,10 +6403,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6497,10 +6497,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 160c709044..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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 and permission checking. - *--------------------------------------------------------------- - */ -static Query * -UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) -{ - Relation viewRel; - List *new_rt; - ParseNamespaceItem *nsitem; - RangeTblEntry *rt_entry1, - *rt_entry2; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index f81c63aa23..e1cd9d0832 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1706,7 +1706,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1824,17 +1825,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index fd1052e5db..af7a6a7e43 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2154,7 +2154,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2170,7 +2170,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2186,7 +2186,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2202,7 +2202,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2220,7 +2220,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3113,7 +3113,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 0a23a39aa2..81d87a2678 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1545,7 +1545,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1597,7 +1597,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1613,7 +1613,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1629,7 +1629,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2114,15 +2114,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index aabc564e2c..a2322ebeec 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 32385bbb0e..e60bdf2dff 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1604,9 +1604,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1627,9 +1627,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1719,8 +1719,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1965,7 +1965,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2017,19 +2017,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 6a56f0b09c..d3e7b23332 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 6cb6388880..f8cb7d6f94 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,50 +1302,50 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1358,22 +1358,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1413,13 +1413,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1437,10 +1437,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1686,23 +1686,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1716,10 +1716,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1787,13 +1787,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1806,57 +1806,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1879,8 +1879,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1889,13 +1889,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2041,26 +2041,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2079,41 +2079,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2123,68 +2123,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2201,19 +2201,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2223,19 +2223,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2274,64 +2274,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indexrelid; -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2516,24 +2516,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3053,7 +3053,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3092,8 +3092,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3105,8 +3105,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3118,8 +3118,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3131,8 +3131,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index d3e02ca63b..00ff5bbfe6 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index ff76ad4d6e..f6f78ec52d 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55b65ef324..bb994a05de 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 0484260281..974b14f859 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.24.1
v10-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 151.4K) download From 207241cda01de936bc8f8a624a2c3b486975feb6 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v10 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 ++++-- contrib/sepgsql/dml.c | 42 ++- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 ++++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 159 +++++++---- src/backend/nodes/copyfuncs.c | 32 ++- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 +- src/backend/nodes/readfuncs.c | 23 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 +++------ src/backend/optimizer/plan/subselect.c | 5 + src/backend/optimizer/prep/prepjointree.c | 10 + src/backend/optimizer/util/inherit.c | 170 ++++++++---- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 62 +++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 289 ++++++++++++++------ src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 182 ++++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 87 +++--- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 52 files changed, 1080 insertions(+), 652 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 56654844e8..a33191de40 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -458,7 +460,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -623,7 +626,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -657,12 +659,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1515,16 +1517,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1816,7 +1817,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1895,6 +1897,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1906,6 +1937,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1913,6 +1945,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1936,6 +1969,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1947,7 +1981,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2139,6 +2174,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2216,6 +2252,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2232,7 +2270,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2618,7 +2657,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2638,13 +2676,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3942,12 +3979,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3959,12 +3996,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 97e61b8043..cb9aeb175d 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 7da7105d44..44b86cdedf 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index db6eb6fae7..9e42904c6b 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1383,7 +1389,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index cd30f15eba..356a93f7c5 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1231,7 +1231,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 80faae985e..8b5bb547fa 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1200,7 +1200,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9625,7 +9626,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9792,7 +9794,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9998,7 +10001,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10176,7 +10180,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12270,7 +12275,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18021,7 +18027,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18948,7 +18955,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..160c709044 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 473d2e00a2..076933b513 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,7 +74,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -553,8 +553,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -566,38 +566,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -605,32 +606,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -664,14 +654,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -696,15 +686,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -712,12 +702,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -772,17 +762,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -814,9 +801,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1855,7 +1843,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1940,7 +1928,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1992,7 +1981,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2099,7 +2089,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 5dd8ab7db2..8b61aebe88 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 90ed1485d1..981d7d3111 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..732f833120 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte, false); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d4f8455a2b..48506a2f5e 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -781,6 +782,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1271,6 +1273,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2463,6 +2484,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2488,12 +2510,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3183,6 +3199,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5178,6 +5195,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index f1002afe7a..c499611316 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -992,6 +992,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2775,6 +2776,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -2800,17 +2802,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3863,6 +3873,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 6bdad462c7..655eb2d888 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -710,6 +711,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -998,6 +1000,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2274,6 +2291,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3079,6 +3097,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3258,6 +3277,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3311,12 +3331,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -4009,6 +4023,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 3f68f7c18d..f994cd9908 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1440,6 +1441,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1503,13 +1505,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1588,6 +1601,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2074,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2848,6 +2863,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index fa069a217c..3097d38d42 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4093,6 +4093,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5743,7 +5746,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index bd09f85aea..705afea0d6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5926,6 +5930,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6053,6 +6058,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index a7b11b7f03..bdf8b66e4d 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -104,9 +104,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -259,7 +257,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -345,10 +343,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -361,29 +366,13 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. - */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -400,73 +389,27 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + glob->finalrelpermlist = + list_concat(glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally, add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); } /* @@ -475,9 +418,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -488,6 +429,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 863e0e24a1..c0b198b4c9 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1507,6 +1507,11 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subselect->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 74823e8437..c1f9610478 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* + * Add subquery's RelPermissionInfos into the upper query. + */ + MergeRelPermissionInfos(parse, subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1269,6 +1274,11 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* + * Add the child query's RelPermissionInfos into the parent query. + */ + MergeRelPermissionInfos(root->parse, subquery->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..4da2b213ed 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte, false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..cd70026f3e 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 61026753a3..c1f290e578 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -516,6 +516,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -548,7 +549,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -667,6 +668,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -892,7 +900,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -908,8 +916,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -936,6 +944,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1094,8 +1103,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1389,6 +1396,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1623,6 +1631,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1869,6 +1878,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2340,6 +2350,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2406,6 +2417,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2424,7 +2436,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2436,7 +2448,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2477,8 +2489,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2765,6 +2777,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3243,9 +3256,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, + false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3301,9 +3322,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d8b14ba7cd..7c10f262e0 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3241,16 +3241,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index cb9e177b5e..f9ea9400c4 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,159 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless there already + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfo(*relpermlist, rte, true); + if (perminfo) + { + Assert(rte->perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = rte->relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Remember the index in the RTE. */ + Assert(rte->perminfoindex == 0); + rte->perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation in the provided + * list, erroring out or returning NULL (depending on missing_ok) if not + * found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok) +{ + RelPermissionInfo *perminfo; + ListCell *lc; + int i; + + Assert(rte->rtekind == RTE_RELATION); + + /* Easy when the RTE already knows. */ + if (rte->perminfoindex > 0) + { + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + + perminfo = (RelPermissionInfo *) list_nth(relpermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + + return perminfo; + } + + /* Looks like the RTE doesn't, so try to find it the hard way. */ + i = 0; + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == rte->relid) + { + /* And set the index in RTE. */ + rte->perminfoindex = i + 1; + return perminfo; + } + i++; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", rte->relid); + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source subquery given in + * src_relpermlist into dest_query, "merging" the contents of any that + * are present in both. + * + * This assumes that the caller has already pulled up the source subquery's + * RTEs into dest_query's rtable, because their perminfoindex would need to + * be updated to reflect their now belonging in the new mereged list. + */ +void +MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + ListCell *l1; + + foreach(l1, dest_query->rtable) + { + RangeTblEntry *dest_rte = (RangeTblEntry *) lfirst(l1); + RelPermissionInfo *dest_perminfo; + + /* + * Only RELATIONs have a RelPermissionInfo. Also ignore any that + * don't match the RelPermissionInfo we're trying to merge. + */ + if (dest_rte->rtekind != RTE_RELATION || + dest_rte->relid != src_perminfo->relid) + continue; + + /* + * The current value of perminfoindex points to a perminfo in the + * source query's relpermlist, which we're trying to merge with + * the destination query's relpermlist. So, reset the index to + * signal to GetRelPermissionInfo(), that gets called via + * AddRelPermissionInfo(), to assign it the index of an entry with + * the same relid in dest_query->relpermlist. + */ + dest_rte->perminfoindex = 0; + dest_perminfo = AddRelPermissionInfo(&dest_query->relpermlist, + dest_rte); + /* "merge" proprties. */ + dest_perminfo->inh = src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 059eeb9e94..6ab35e8a16 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1375,6 +1375,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1389,7 +1390,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1421,12 +1425,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index cd946c7692..e6117823dd 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3013,9 +3014,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3071,6 +3069,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3113,8 +3112,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 82dcffc2db..3e1c89a7d9 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -491,6 +492,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1778,7 +1781,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1826,7 +1829,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1836,14 +1839,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 5fddab3a3d..ff07b26d29 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -899,7 +899,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..1a9e5c8524 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 4eeed580b1..f81c63aa23 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,23 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(sub_action, parsetree->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1590,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1619,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1703,8 +1706,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1745,18 +1747,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1843,28 +1833,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1893,8 +1864,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3035,6 +3010,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3242,57 +3222,60 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte, + false); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3396,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3407,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3682,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3693,6 +3675,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry, + false); /* * We can use NoLock here since either the parser or @@ -3778,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index f0a046d65a..03f3881997 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ca48395d5c..cc68e68f1d 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 01d4c22cfc..4590e1148c 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1367,8 +1367,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1386,32 +1386,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1421,9 +1415,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 1fbb0b28c3..934e21eda4 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5137,7 +5137,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5189,7 +5190,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5268,7 +5270,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5316,7 +5319,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5377,6 +5381,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5385,7 +5390,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5454,7 +5460,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 82925b4b63..4dcd7a6051 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 44dd73fc80..b13709c859 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -528,6 +528,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -575,6 +583,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 5d075f0c34..a422955463 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 2f618cb229..1151d39e96 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -147,6 +147,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -949,37 +951,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1033,11 +1004,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1157,14 +1134,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1f3845b3fe..4b8ce415a1 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -730,7 +732,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 0b518ce6b2..0abc993369 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -651,6 +655,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index eb96b2cfc1..6b612cc37e 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 8c859d0d0e..34f101077a 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -233,7 +235,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 06dc27995b..6168512b99 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte); +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-03-23 09:49  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Amit Langote <amitlangote09@gmail.com>
  2 siblings, 0 replies; 97+ messages in thread

From: Alvaro Herrera @ 2022-03-23 09:49 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 2022-Mar-23, Amit Langote wrote:

> As the changes being made with the patch are non-trivial and the patch
> hasn't been reviewed very significantly since Alvaro's comments back
> in Sept 2021 which I've since addressed, I'm thinking of pushing this
> one into the version 16 dev cycle.

Let's not get ahead of ourselves.  The commitfest is not yet over.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"La virtud es el justo medio entre dos defectos" (Aristóteles)





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-03-23 21:02  Zhihong Yu <zyu@yugabyte.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  2 siblings, 0 replies; 97+ messages in thread

From: Zhihong Yu @ 2022-03-23 21:02 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Mar 23, 2022 at 12:03 AM Amit Langote <amitlangote09@gmail.com>
wrote:

> On Mon, Mar 14, 2022 at 4:36 PM Amit Langote <amitlangote09@gmail.com>
> wrote:
> > Also needed fixes when rebasing.
>
> Needed another rebase.
>
> As the changes being made with the patch are non-trivial and the patch
> hasn't been reviewed very significantly since Alvaro's comments back
> in Sept 2021 which I've since addressed, I'm thinking of pushing this
> one into the version 16 dev cycle.
>
> --
> Amit Langote
> EDB: http://www.enterprisedb.com

Hi,
For patch 1:

bq. makes permissions-checking needlessly expensive when many inheritance
children are added to the range range

'range' is repeated in the above sentence.

+ExecCheckOneRelPerms(RelPermissionInfo *perminfo)

Since RelPermissionInfo is for one relation, I think the 'One' in func name
can be dropped.

+       else                    /* this isn't a child result rel */
+           resultRelInfo->ri_RootToChildMap = NULL;
...
+       resultRelInfo->ri_RootToChildMapValid = true;

Should the assignment of true value be moved into the if block (in the else
block, ri_RootToChildMap is assigned NULL) ?

+   /* Looks like the RTE doesn't, so try to find it the hard way. */

doesn't -> doesn't know

Cheers

^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-03-24 19:46  David Rowley <dgrowleyml@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  2 siblings, 1 reply; 97+ messages in thread

From: David Rowley @ 2022-03-24 19:46 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, 23 Mar 2022 at 20:03, Amit Langote <amitlangote09@gmail.com> wrote:
>
> On Mon, Mar 14, 2022 at 4:36 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > Also needed fixes when rebasing.
>
> Needed another rebase.

I had a look at the v10-0001 patch. I agree that it seems to be a good
idea to separate out the required permission checks rather than having
the Bitmapset to index the interesting range table entries.

One thing that I could just not determine from looking at the patch
was if there's meant to be just 1 RelPermissionInfo per RTE or per rel
Oid. None of the comments helped me understand this and
MergeRelPermissionInfos() seems to exist that appears to try and
uniquify this to some extent.  I just see no code that does this
process for a single query level. I've provided more detail on this in
#5 below.

Here's my complete review of v10-0001:

1. ExecCheckPermisssions -> ExecCheckPermissions

2. I think you'll want to move the userid field away from below a
comment that claims the following fields are for foreign tables only.

  /* Information about foreign tables and foreign joins */
  Oid serverid; /* identifies server for the table or join */
- Oid userid; /* identifies user to check access as */
+ Oid userid; /* identifies user to check access as; set
+ * in non-foreign table relations too! */

3. This should use READ_OID_FIELD()

READ_INT_FIELD(checkAsUser);

and this one:

READ_INT_FIELD(relid);

4.  This looks wrong:

- rel->userid = rte->checkAsUser;
+ if (rte->rtekind == RTE_RELATION)
+ {
+ /* otherrels use the root parent's value. */
+ rel->userid = parent ? parent->userid :
+ GetRelPermissionInfo(root->parse->relpermlist,
+ rte, false)->checkAsUser;
+ }

If 'parent' is false then you'll assign the result of
GetRelPermissionInfo (a RelPermissionInfo *) to an Oid.

5. I'm not sure if there's a case that can break this one, but I'm not
very confident that there's not one:

I'm not sure I agree with how you've coded GetRelPermissionInfo().
You're searching for a RelPermissionInfo based on the table's Oid.  If
you have multiple RelPermissionInfos for the same Oid then this will
just find the first one and return it, but that might not be the one
for the RangeTblEntry in question.

Here's an example of the sort of thing that could have problems with this:

postgres=# create role bob;
CREATE ROLE
postgres=# create table ab (a int, b int);
CREATE TABLE
postgres=# grant all (a) on table ab to bob;
GRANT
postgres=# set session authorization bob;
SET
postgres=> update ab set a = (select b from ab);
ERROR:  permission denied for table ab

The patch does correctly ERROR out here on permission failure, but as
far as I can see, that's just due to the fact that we're checking the
permissions of all items in the PlannedStmt.relpermlist List.  If
there had been code that had tried to find the RelPermissionInfo based
on the relation's Oid then we'd have accidentally found that we only
need an UPDATE permission on (a).  SELECT on (b) wouldn't have been
checked.

As far as I can see, to fix that you'd either need to store the RTI of
the RelPermissionInfo and lookup based on that, or you'd have to
bms_union() all the columns and bitwise OR the required permissions
and ensure you only have 1 RelPermissionInfo per Oid.

The fact that I have two entries when I debug InitPlan() seems to
disagree with what the comment in AddRelPermissionInfo() is claiming
should happen:

/*
* To prevent duplicate entries for a given relation, check if already in
* the list.
*/

I'm not clear on if the list is meant to be unique on Oid or not.

6. acesss?

- * Set flags and access permissions.
+ * Set flags and initialize acesss permissions.

7. I was expecting to see an |= here:

+ /* "merge" proprties. */
+ dest_perminfo->inh = src_perminfo->inh;

Why is a plain assignment ok?

8. Some indentation problems here:

@@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view)

  base_rt_index = rtr->rtindex;
  base_rte = rt_fetch(base_rt_index, viewquery->rtable);
+base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte,
+ false);

9. You can use foreach_current_index(lc) + 1 in:

+ i = 0;
+ foreach(lc, relpermlist)
+ {
+ perminfo = (RelPermissionInfo *) lfirst(lc);
+ if (perminfo->relid == rte->relid)
+ {
+ /* And set the index in RTE. */
+ rte->perminfoindex = i + 1;
+ return perminfo;
+ }
+ i++;
+ }

10. I think the double quote is not in the correct place in this comment:

  List    *finalrtable; /* "flat" rangetable for executor */

+ List    *finalrelpermlist; /* "flat list of RelPermissionInfo "*/


11. Looks like an accidental change:

+++ b/src/include/optimizer/planner.h
@@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel,

 extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr);

+

12. These need to be broken into multiple lines:

+extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist,
RangeTblEntry *rte);
+extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist);
+extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist,
RangeTblEntry *rte, bool missing_ok);

David





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-03-31 03:16  Amit Langote <amitlangote09@gmail.com>
  parent: David Rowley <dgrowleyml@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-03-31 03:16 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Mar 25, 2022 at 4:46 AM David Rowley <dgrowleyml@gmail.com> wrote:
> I had a look at the v10-0001 patch. I agree that it seems to be a good
> idea to separate out the required permission checks rather than having
> the Bitmapset to index the interesting range table entries.

Thanks David for taking a look at this.

> One thing that I could just not determine from looking at the patch
> was if there's meant to be just 1 RelPermissionInfo per RTE or per rel
> Oid.

It's the latter.

> None of the comments helped me understand this

I agree that the comment above the RelPermissionInfo struct definition
missed mentioning this really important bit.  I've tried fixing that
as:

@@ -1142,7 +1142,9 @@ typedef struct RangeTblEntry
  *         Per-relation information for permission checking. Added to the query
  *         by the parser when populating the query range table and subsequently
  *         editorialized on by the rewriter and the planner.  There is an entry
- *         each for all RTE_RELATION entries present in the range table.
+ *         each for all RTE_RELATION entries present in the range table, though
+ *         different RTEs for the same relation share the
RelPermissionInfo, that
+ *         is, there is only one RelPermissionInfo containing a given relid.

> and
> MergeRelPermissionInfos() seems to exist that appears to try and
> uniquify this to some extent.  I just see no code that does this
> process for a single query level. I've provided more detail on this in
> #5 below.
>
> Here's my complete review of v10-0001:
>
> 1. ExecCheckPermisssions -> ExecCheckPermissions

Fixed.

> 2. I think you'll want to move the userid field away from below a
> comment that claims the following fields are for foreign tables only.
>
>   /* Information about foreign tables and foreign joins */
>   Oid serverid; /* identifies server for the table or join */
> - Oid userid; /* identifies user to check access as */
> + Oid userid; /* identifies user to check access as; set
> + * in non-foreign table relations too! */

Actually, I realized that the comment should not have been touched to
begin with.  Reverted.

> 3. This should use READ_OID_FIELD()
>
> READ_INT_FIELD(checkAsUser);
>
> and this one:
>
> READ_INT_FIELD(relid);

Fixed.

> 4.  This looks wrong:
>
> - rel->userid = rte->checkAsUser;
> + if (rte->rtekind == RTE_RELATION)
> + {
> + /* otherrels use the root parent's value. */
> + rel->userid = parent ? parent->userid :
> + GetRelPermissionInfo(root->parse->relpermlist,
> + rte, false)->checkAsUser;
> + }
>
> If 'parent' is false then you'll assign the result of
> GetRelPermissionInfo (a RelPermissionInfo *) to an Oid.

Hmm, I don't see a problem, because what's being assigned is
`GetRelPermissionInfo(...)->checkAsUser`.

Anyway, I rewrote the block more verbosely as:

    if (rte->rtekind == RTE_RELATION)
    {
-       /* otherrels use the root parent's value. */
-       rel->userid = parent ? parent->userid :
-           GetRelPermissionInfo(root->parse->relpermlist,
-                                rte, false)->checkAsUser;
+       /*
+        * Get the userid from the relation's RelPermissionInfo, though
+        * only the tables mentioned in query are assigned RelPermissionInfos.
+        * Child relations (otherrels) simply use the parent's value.
+        */
+       if (parent == NULL)
+       {
+           RelPermissionInfo *perminfo =
+               GetRelPermissionInfo(root->parse->relpermlist, rte, false);
+
+           rel->userid = perminfo->checkAsUser;
+       }
+       else
+           rel->userid = parent->userid;
    }
+   else
+       rel->userid = InvalidOid;

> 5. I'm not sure if there's a case that can break this one, but I'm not
> very confident that there's not one:
>
> I'm not sure I agree with how you've coded GetRelPermissionInfo().
> You're searching for a RelPermissionInfo based on the table's Oid.  If
> you have multiple RelPermissionInfos for the same Oid then this will
> just find the first one and return it, but that might not be the one
> for the RangeTblEntry in question.
>
> Here's an example of the sort of thing that could have problems with this:
>
> postgres=# create role bob;
> CREATE ROLE
> postgres=# create table ab (a int, b int);
> CREATE TABLE
> postgres=# grant all (a) on table ab to bob;
> GRANT
> postgres=# set session authorization bob;
> SET
> postgres=> update ab set a = (select b from ab);
> ERROR:  permission denied for table ab
>
> The patch does correctly ERROR out here on permission failure, but as
> far as I can see, that's just due to the fact that we're checking the
> permissions of all items in the PlannedStmt.relpermlist List.  If
> there had been code that had tried to find the RelPermissionInfo based
> on the relation's Oid then we'd have accidentally found that we only
> need an UPDATE permission on (a).  SELECT on (b) wouldn't have been
> checked.
>
> As far as I can see, to fix that you'd either need to store the RTI of
> the RelPermissionInfo and lookup based on that, or you'd have to
> bms_union() all the columns and bitwise OR the required permissions
> and ensure you only have 1 RelPermissionInfo per Oid.
>
> The fact that I have two entries when I debug InitPlan() seems to
> disagree with what the comment in AddRelPermissionInfo() is claiming
> should happen:
>
> /*
> * To prevent duplicate entries for a given relation, check if already in
> * the list.
> */
>
> I'm not clear on if the list is meant to be unique on Oid or not.

Yeah, it is, but it seems that the code I added in
add_rtes_to_flat_rtable() to accumulate various subplans' relpermlists
into finalrelpermlist didn't actually bother to unique'ify the list.
It used list_concat() to combine finalrelpermlist and a given
subplan's relpermlist, instead of MergeRelPemissionInfos to merge the
two lists.

I've fixed that in the attached and can now see that the plan for the
query in your example ends up with just one RelPermissionInfo which
combines the permissions and column bitmapsets for all operations.

> 6. acesss?
>
> - * Set flags and access permissions.
> + * Set flags and initialize acesss permissions.
>
> 7. I was expecting to see an |= here:
>
> + /* "merge" proprties. */
> + dest_perminfo->inh = src_perminfo->inh;
>
> Why is a plain assignment ok?

You're perhaps right that |= is correct.  I forget the details but I
think I added 'inh' field to RelPemissionInfo to get some tests in
sepgsql contrib module to pass and those tests apparently didn't mind
the current coding.

> 8. Some indentation problems here:
>
> @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view)
>
>   base_rt_index = rtr->rtindex;
>   base_rte = rt_fetch(base_rt_index, viewquery->rtable);
> +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte,
> + false);

Fixed.

>
> 9. You can use foreach_current_index(lc) + 1 in:
>
> + i = 0;
> + foreach(lc, relpermlist)
> + {
> + perminfo = (RelPermissionInfo *) lfirst(lc);
> + if (perminfo->relid == rte->relid)
> + {
> + /* And set the index in RTE. */
> + rte->perminfoindex = i + 1;
> + return perminfo;
> + }
> + i++;
> + }

Oh, nice tip.  Done.

> 10. I think the double quote is not in the correct place in this comment:
>
>   List    *finalrtable; /* "flat" rangetable for executor */
>
> + List    *finalrelpermlist; /* "flat list of RelPermissionInfo "*/
>
>
> 11. Looks like an accidental change:
>
> +++ b/src/include/optimizer/planner.h
> @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel,
>
>  extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr);
>
> +
>
> 12. These need to be broken into multiple lines:
>
> +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist,
> RangeTblEntry *rte);
> +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist);
> +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist,
> RangeTblEntry *rte, bool missing_ok);

All fixed.

v11 attached.

--
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v11-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 119.6K) download From 6ceefde7fb936324bfb3ae0db5951c0342fc9397 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v11 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 34 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 706 +++++++++--------- src/test/regress/expected/sqljson.out | 6 +- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 27 files changed, 716 insertions(+), 795 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index f210f91188..005f30299d 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6403,10 +6403,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6497,10 +6497,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b97b8b0435..2cbc53b42c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 f0958f03a0..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fca2a1eaf0..6225ccba30 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1859,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index af5d6fa5a3..a16b29d756 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2153,7 +2153,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2169,7 +2169,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2185,7 +2185,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2201,7 +2201,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2219,7 +2219,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3204,7 +3204,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 0a23a39aa2..81d87a2678 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1545,7 +1545,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1597,7 +1597,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1613,7 +1613,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1629,7 +1629,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2114,15 +2114,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 07473dd660..bc5d230e03 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 32385bbb0e..e60bdf2dff 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1604,9 +1604,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1627,9 +1627,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1719,8 +1719,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1965,7 +1965,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2017,19 +2017,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 6a56f0b09c..d3e7b23332 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 423b9b99fb..598cf30ec9 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,13 +1419,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1451,10 +1451,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1700,23 +1700,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1730,10 +1730,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1801,13 +1801,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1820,57 +1820,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1893,8 +1893,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1903,13 +1903,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2055,26 +2055,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2093,41 +2093,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2137,68 +2137,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2215,19 +2215,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2237,19 +2237,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2293,64 +2293,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2535,24 +2535,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3072,7 +3072,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3111,8 +3111,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3124,8 +3124,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3137,8 +3137,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3150,8 +3150,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 27dca7815a..564662c9ae 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -683,7 +683,7 @@ SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING t FROM generate_series(1,5) i; \sv json_objectagg_view CREATE OR REPLACE VIEW public.json_objectagg_view AS - SELECT JSON_OBJECTAGG(i.i : ('111'::text || i.i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.i > 3) AS "json_objectagg" + SELECT JSON_OBJECTAGG(i : ('111'::text || i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i > 3) AS "json_objectagg" FROM generate_series(1, 5) i(i) DROP VIEW json_objectagg_view; -- Test JSON_ARRAYAGG deparsing @@ -719,7 +719,7 @@ SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text FROM generate_series(1,5) i; \sv json_arrayagg_view CREATE OR REPLACE VIEW public.json_arrayagg_view AS - SELECT JSON_ARRAYAGG(('111'::text || i.i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.i > 3) AS "json_arrayagg" + SELECT JSON_ARRAYAGG(('111'::text || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3) AS "json_arrayagg" FROM generate_series(1, 5) i(i) DROP VIEW json_arrayagg_view; -- Test JSON_ARRAY(subquery) deparsing @@ -937,7 +937,7 @@ SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT \sv is_json_view CREATE OR REPLACE VIEW public.is_json_view AS SELECT '1'::text IS JSON AS "any", - ('1'::text || i.i) IS JSON SCALAR AS scalar, + ('1'::text || i) IS JSON SCALAR AS scalar, NOT '[]'::text IS JSON ARRAY AS "array", '{}'::text IS JSON OBJECT WITH UNIQUE KEYS AS object FROM generate_series(1, 3) i(i) diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index cd812336f2..b09603dc98 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7c6de7cc07..bea54c91cd 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55b65ef324..bb994a05de 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 0484260281..974b14f859 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.24.1
v11-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 158.6K) download From 1c6a935264be115573c7a17eb596a1a2e3cff5e3 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v11 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 +++-- contrib/sepgsql/dml.c | 42 +-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 105 +++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 159 ++++++--- src/backend/nodes/copyfuncs.c | 32 +- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 +- src/backend/nodes/readfuncs.c | 21 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 128 +++---- src/backend/optimizer/plan/subselect.c | 6 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 170 +++++++--- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 60 +++- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 357 +++++++++++++++----- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 25 +- src/backend/rewrite/rewriteHandler.c | 189 +++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 89 +++-- src/include/nodes/pathnodes.h | 2 + src/include/nodes/plannodes.h | 4 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 8 + src/include/rewrite/rewriteHandler.h | 2 +- 53 files changed, 1173 insertions(+), 682 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 56654844e8..435afe5a62 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -458,7 +460,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -623,7 +626,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -657,12 +659,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1515,16 +1517,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1816,7 +1817,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1895,6 +1897,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1906,6 +1937,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1913,6 +1945,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1936,6 +1969,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1947,7 +1981,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2139,6 +2174,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2216,6 +2252,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2232,7 +2270,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2618,7 +2657,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2638,13 +2676,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3942,12 +3979,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3959,12 +3996,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 97e61b8043..cb9aeb175d 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 7a0c897cc9..ff6213f371 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index db6eb6fae7..9e42904c6b 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1383,7 +1389,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index cd30f15eba..356a93f7c5 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1231,7 +1231,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 51b4a00d50..8cbb1cb830 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1201,7 +1201,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9626,7 +9627,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9793,7 +9795,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9999,7 +10002,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10177,7 +10181,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12271,7 +12276,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18046,7 +18052,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18973,7 +18980,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..f0958f03a0 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,7 +353,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ef2fd46092..836e5ef344 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,7 +74,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -567,38 +567,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -606,32 +607,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 9a0d5d59ef..815577e5a2 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index aca42ca5b8..8155c7de59 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -576,7 +576,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -633,7 +634,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -775,7 +777,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -873,7 +876,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1135,7 +1139,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..e9cf39dafb 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index c42d2ed814..9ec27e0bd7 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -782,6 +783,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1272,6 +1274,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2802,6 +2823,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2827,12 +2849,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3551,6 +3567,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5563,6 +5580,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index f6dd61370c..0103e08c36 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1245,6 +1245,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -3043,6 +3044,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -3068,17 +3070,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -4187,6 +4197,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 6e39590730..afd0795bfb 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -711,6 +712,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -999,6 +1001,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2390,6 +2407,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3195,6 +3213,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3402,6 +3421,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3455,12 +3475,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -4153,6 +4167,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index c94b2561f0..5f613235a3 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1637,6 +1638,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1700,13 +1702,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_OID_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); READ_OID_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1785,6 +1798,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2272,6 +2286,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -3050,6 +3065,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 179c87c671..e9efb3edef 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4095,6 +4095,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5745,7 +5748,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 547fda20a2..bcab1f567c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5988,6 +5992,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6115,6 +6120,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index bf4c722c02..59d84a7613 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -24,6 +24,7 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -104,9 +105,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -259,7 +258,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -345,10 +344,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -356,34 +362,14 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) /* * Add the query's own RTEs to the flattened rangetable. - * - * 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. - */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -400,73 +386,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + MergeRelPermissionInfos(&glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; + /* Now merge the main query's RelPermissionInfos into the global list. */ + MergeRelPermissionInfos(&glob->finalrelpermlist, root->parse->relpermlist); - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally update the flat rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(glob->finalrtable, + glob->finalrelpermlist); } /* @@ -475,9 +417,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -488,6 +428,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 863e0e24a1..7b55f16d76 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1507,6 +1507,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subselect->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 0bd99acf83..ae36b47e58 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1211,6 +1204,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1349,6 +1348,13 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* Add the child query's RelPermissionInfos into the parent query. */ + MergeRelPermissionInfos(&root->parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(root->parse->rtable, + root->parse->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..919e2ec1e9 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..64a00b541b 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RelPermissionInfo, though + * only the tables mentioned in query are assigned RelPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 0144284aa3..dd386ac4ae 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -550,7 +551,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -669,6 +670,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +918,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +946,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1105,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1398,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1625,6 +1633,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1871,6 +1880,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2342,6 +2352,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2408,6 +2419,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2426,7 +2438,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2438,7 +2450,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2479,8 +2491,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2767,6 +2779,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3245,9 +3258,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3303,9 +3323,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d8b14ba7cd..7c10f262e0 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3241,16 +3241,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index 5d0035a12b..281ac6cbb4 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -209,6 +209,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) exprLocation(stmt->sourceRelation)); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; /* * Transform the join condition. This includes references to the target @@ -281,7 +282,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -340,7 +341,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -354,8 +355,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 7efa5f15d7..3ab6c8ef67 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -82,6 +82,12 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, List **colnames, List **colvars); static int specialAttNum(const char *attname); static bool isQueryUsingTempRelation_walker(Node *node, void *context); +static RelPermissionInfo *AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex); +static RelPermissionInfo *GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok); +static Index GetRelPermissionInfoIndex(List *relpermlist, Oid relid); /* @@ -1021,10 +1027,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1244,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1286,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1430,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1467,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1476,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1497,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1534,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1558,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1567,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1588,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1639,21 +1658,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1946,20 +1959,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1971,7 +1977,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2017,20 +2023,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2105,19 +2104,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2196,19 +2189,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2223,6 +2210,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2346,19 +2334,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2472,16 +2454,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2493,7 +2472,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3114,6 +3093,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3131,7 +3111,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3680,3 +3663,221 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless one with the same OID is found in it + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + Assert(rte->rtekind == RTE_RELATION); + + Assert(rte->perminfoindex == 0); + return AddRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex); +} + +/* + * AddRelPermissionInfoInternal + * Sub-routine of AddRelPermissionInfo that does the actual work + */ +static RelPermissionInfo * +AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex) +{ + RelPermissionInfo *perminfo; + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfoInternal(*relpermlist, relid, perminfoindex, + true); + if (perminfo) + { + Assert(*perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Note its index. */ + *perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Find RelPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + perminfo = GetRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex, false); + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} + +/* + * GetRelPermissionInfoInternal + * Sub-routine of GetRelPermissionInfo that does the actual work + * + * If *perminfoindex is 0, the list is scanned to find one with given relid. + * If found, *perminfoindex is set to its 1-based index in the list. + * + * If *perminfoindex is already valid (> 0), it means that the caller expects + * to find the entry it's looking for at that location in the list. + */ +static RelPermissionInfo * +GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok) +{ + RelPermissionInfo *perminfo; + + if (*perminfoindex == 0) + { + ListCell *lc; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == relid) + { + *perminfoindex = foreach_current_index(lc) + 1; + return perminfo; + } + } + } + else if (*perminfoindex > 0) + { + + perminfo = (RelPermissionInfo *) + list_nth(relpermlist, *perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * GetRelPermissionInfoIndex + * Returns a 1-based index of the RelPermissionInfo of matching relid if + * found in the given list + * + * 0 indicates that one was not found. + */ +static Index +GetRelPermissionInfoIndex(List *relpermlist, Oid relid) +{ + ListCell *lc; + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(lc); + + if (perminfo->relid == relid) + return foreach_current_index(lc) + 1; + } + + return 0; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source query (src_relpermlist) + * into the destination query's list (*dest_relpermlist), "merging" + * properties of any that are present in both. + * + * Caller must subsequently call ReassignRangeTablePermInfoIndexes() on the + * source query's range table. + */ +void +MergeRelPermissionInfos(List **dest_relpermlist, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *dest_perminfo; + Index ignored = 0; + + dest_perminfo = AddRelPermissionInfoInternal(dest_relpermlist, + src_perminfo->relid, + &ignored); + + dest_perminfo->inh |= src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } +} + +/* + * ReassignRangeTablePermInfoIndexes + * Updates perminfoindex of the relation RTEs in rtable so that they reflect + * their respective RelPermissionInfo entry's current position in permlist. + * + * This is provided for the sites that use MergeRelPermissionInfos() to merge + * the RelPermissionInfo entries of two queries. + */ +void +ReassignRangeTablePermInfoIndexes(List *rtable, List *relpermlist) +{ + ListCell *l; + + foreach(l, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + /* + * Only RELATIONs would have been assigned a RelPermissionInfo and that + * too only those that are mentioned in the query (not inheritance + * child relations that are added afterwards), so we also check that + * the RTE's existing index is valid. + */ + if (rte->rtekind != RTE_RELATION && rte->perminfoindex > 0) + continue; + rte->perminfoindex = GetRelPermissionInfoIndex(relpermlist, rte->relid); + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 62d5d7d439..0b7f1c80b3 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index cd946c7692..e6117823dd 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3013,9 +3014,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3071,6 +3069,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3113,8 +3112,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index f3868b3e1f..6745972d76 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -157,6 +157,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -492,6 +493,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1830,7 +1833,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1878,7 +1881,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1888,14 +1891,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 893833ea83..ff58ea63ca 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1053,7 +1053,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..9144843fd5 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -787,14 +788,7 @@ 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 rtable entries. - * - * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD - * RTE entry will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * field to the given userid in all RelPermissionInfos of the query. */ void setRuleCheckAsUser(Node *node, Oid userid) @@ -821,18 +815,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RelPermissionInfos for this query. */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 29ae27e5e3..fca2a1eaf0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,27 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(&sub_action->relpermlist, parsetree->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(sub_action->rtable, + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1594,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1623,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3174,6 +3150,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index a233dd4758..d0a292d46c 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ca48395d5c..cc68e68f1d 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 01d4c22cfc..4590e1148c 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1367,8 +1367,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1386,32 +1386,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1421,9 +1415,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 1fbb0b28c3..faa2086215 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5137,7 +5137,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5189,7 +5189,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5268,7 +5268,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5316,7 +5316,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5377,6 +5377,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5385,7 +5386,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5454,7 +5455,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index a15ce9edb1..982c4ce33c 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -846,8 +846,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RelPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 873772f188..222acf6e88 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -602,6 +603,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index cbbcff81d2..7b04008a12 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -548,6 +548,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -595,6 +603,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index d48147abee..73a0bf8cf3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -91,6 +91,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 0ff4ba0884..1961964395 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -147,6 +147,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -953,37 +955,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1037,11 +1008,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1161,14 +1138,60 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table, though + * different RTEs for the same relation share the RelPermissionInfo, that + * is, there is only one RelPermissionInfo containing a given relid. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 365000bdcd..7b03f8321a 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat" list of RelPermissionInfo */ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 50ef3dda05..66fb82ab01 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -66,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -654,6 +657,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index cf9c759025..e573b1620f 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -181,6 +181,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +236,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -268,6 +271,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index de21c3c649..923add1176 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,13 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, + RangeTblEntry *rte); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, + RangeTblEntry *rte); +extern void MergeRelPermissionInfos(List **dest_relpermlist, + List *src_relpermlist); +extern void ReassignRangeTablePermInfoIndexes(List *rtable, + List *relpermlist); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-04-05 14:26  Greg Stark <stark@mit.edu>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Greg Stark @ 2022-04-05 14:26 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: David Rowley <dgrowleyml@gmail.com>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

This is failing regression tests. I don't understand how this patch
could be affecting this test though. Perhaps it's a problem with the
json patches that were committed recently -- but they don't seem to be
causing other patches to fail.


diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/jsonb_sqljson.out
/tmp/cirrus-ci-build/src/test/regress/results/jsonb_sqljson.out
--- /tmp/cirrus-ci-build/src/test/regress/expected/jsonb_sqljson.out
2022-04-05 12:15:40.590168291 +0000
+++ /tmp/cirrus-ci-build/src/test/regress/results/jsonb_sqljson.out
2022-04-05 12:20:17.338045137 +0000
@@ -1159,37 +1159,37 @@
  );
 \sv jsonb_table_view
 CREATE OR REPLACE VIEW public.jsonb_table_view AS
- SELECT "json_table".id,
-    "json_table".id2,
-    "json_table"."int",
-    "json_table".text,
-    "json_table"."char(4)",
-    "json_table".bool,
-    "json_table"."numeric",
-    "json_table".domain,
-    "json_table".js,
-    "json_table".jb,
-    "json_table".jst,
-    "json_table".jsc,
-    "json_table".jsv,
-    "json_table".jsb,
-    "json_table".jsbq,
-    "json_table".aaa,
-    "json_table".aaa1,
-    "json_table".exists1,
-    "json_table".exists2,
-    "json_table".exists3,
-    "json_table".js2,
-    "json_table".jsb2w,
-    "json_table".jsb2q,
-    "json_table".ia,
-    "json_table".ta,
-    "json_table".jba,
-    "json_table".a1,
-    "json_table".b1,
-    "json_table".a11,
-    "json_table".a21,
-    "json_table".a22
+ SELECT id,
+    id2,
+    "int",
+    text,
+    "char(4)",
+    bool,
+    "numeric",
+    domain,
+    js,
+    jb,
+    jst,
+    jsc,
+    jsv,
+    jsb,
+    jsbq,
+    aaa,
+    aaa1,
+    exists1,
+    exists2,
+    exists3,
+    js2,
+    jsb2w,
+    jsb2q,
+    ia,
+    ta,
+    jba,
+    a1,
+    b1,
+    a11,
+    a21,
+    a22
    FROM JSON_TABLE(
             'null'::jsonb, '$[*]'
             PASSING


On Wed, 30 Mar 2022 at 23:16, Amit Langote <amitlangote09@gmail.com> wrote:
>
> On Fri, Mar 25, 2022 at 4:46 AM David Rowley <dgrowleyml@gmail.com> wrote:
> > I had a look at the v10-0001 patch. I agree that it seems to be a good
> > idea to separate out the required permission checks rather than having
> > the Bitmapset to index the interesting range table entries.
>
> Thanks David for taking a look at this.
>
> > One thing that I could just not determine from looking at the patch
> > was if there's meant to be just 1 RelPermissionInfo per RTE or per rel
> > Oid.
>
> It's the latter.
>
> > None of the comments helped me understand this
>
> I agree that the comment above the RelPermissionInfo struct definition
> missed mentioning this really important bit.  I've tried fixing that
> as:
>
> @@ -1142,7 +1142,9 @@ typedef struct RangeTblEntry
>   *         Per-relation information for permission checking. Added to the query
>   *         by the parser when populating the query range table and subsequently
>   *         editorialized on by the rewriter and the planner.  There is an entry
> - *         each for all RTE_RELATION entries present in the range table.
> + *         each for all RTE_RELATION entries present in the range table, though
> + *         different RTEs for the same relation share the
> RelPermissionInfo, that
> + *         is, there is only one RelPermissionInfo containing a given relid.
>
> > and
> > MergeRelPermissionInfos() seems to exist that appears to try and
> > uniquify this to some extent.  I just see no code that does this
> > process for a single query level. I've provided more detail on this in
> > #5 below.
> >
> > Here's my complete review of v10-0001:
> >
> > 1. ExecCheckPermisssions -> ExecCheckPermissions
>
> Fixed.
>
> > 2. I think you'll want to move the userid field away from below a
> > comment that claims the following fields are for foreign tables only.
> >
> >   /* Information about foreign tables and foreign joins */
> >   Oid serverid; /* identifies server for the table or join */
> > - Oid userid; /* identifies user to check access as */
> > + Oid userid; /* identifies user to check access as; set
> > + * in non-foreign table relations too! */
>
> Actually, I realized that the comment should not have been touched to
> begin with.  Reverted.
>
> > 3. This should use READ_OID_FIELD()
> >
> > READ_INT_FIELD(checkAsUser);
> >
> > and this one:
> >
> > READ_INT_FIELD(relid);
>
> Fixed.
>
> > 4.  This looks wrong:
> >
> > - rel->userid = rte->checkAsUser;
> > + if (rte->rtekind == RTE_RELATION)
> > + {
> > + /* otherrels use the root parent's value. */
> > + rel->userid = parent ? parent->userid :
> > + GetRelPermissionInfo(root->parse->relpermlist,
> > + rte, false)->checkAsUser;
> > + }
> >
> > If 'parent' is false then you'll assign the result of
> > GetRelPermissionInfo (a RelPermissionInfo *) to an Oid.
>
> Hmm, I don't see a problem, because what's being assigned is
> `GetRelPermissionInfo(...)->checkAsUser`.
>
> Anyway, I rewrote the block more verbosely as:
>
>     if (rte->rtekind == RTE_RELATION)
>     {
> -       /* otherrels use the root parent's value. */
> -       rel->userid = parent ? parent->userid :
> -           GetRelPermissionInfo(root->parse->relpermlist,
> -                                rte, false)->checkAsUser;
> +       /*
> +        * Get the userid from the relation's RelPermissionInfo, though
> +        * only the tables mentioned in query are assigned RelPermissionInfos.
> +        * Child relations (otherrels) simply use the parent's value.
> +        */
> +       if (parent == NULL)
> +       {
> +           RelPermissionInfo *perminfo =
> +               GetRelPermissionInfo(root->parse->relpermlist, rte, false);
> +
> +           rel->userid = perminfo->checkAsUser;
> +       }
> +       else
> +           rel->userid = parent->userid;
>     }
> +   else
> +       rel->userid = InvalidOid;
>
> > 5. I'm not sure if there's a case that can break this one, but I'm not
> > very confident that there's not one:
> >
> > I'm not sure I agree with how you've coded GetRelPermissionInfo().
> > You're searching for a RelPermissionInfo based on the table's Oid.  If
> > you have multiple RelPermissionInfos for the same Oid then this will
> > just find the first one and return it, but that might not be the one
> > for the RangeTblEntry in question.
> >
> > Here's an example of the sort of thing that could have problems with this:
> >
> > postgres=# create role bob;
> > CREATE ROLE
> > postgres=# create table ab (a int, b int);
> > CREATE TABLE
> > postgres=# grant all (a) on table ab to bob;
> > GRANT
> > postgres=# set session authorization bob;
> > SET
> > postgres=> update ab set a = (select b from ab);
> > ERROR:  permission denied for table ab
> >
> > The patch does correctly ERROR out here on permission failure, but as
> > far as I can see, that's just due to the fact that we're checking the
> > permissions of all items in the PlannedStmt.relpermlist List.  If
> > there had been code that had tried to find the RelPermissionInfo based
> > on the relation's Oid then we'd have accidentally found that we only
> > need an UPDATE permission on (a).  SELECT on (b) wouldn't have been
> > checked.
> >
> > As far as I can see, to fix that you'd either need to store the RTI of
> > the RelPermissionInfo and lookup based on that, or you'd have to
> > bms_union() all the columns and bitwise OR the required permissions
> > and ensure you only have 1 RelPermissionInfo per Oid.
> >
> > The fact that I have two entries when I debug InitPlan() seems to
> > disagree with what the comment in AddRelPermissionInfo() is claiming
> > should happen:
> >
> > /*
> > * To prevent duplicate entries for a given relation, check if already in
> > * the list.
> > */
> >
> > I'm not clear on if the list is meant to be unique on Oid or not.
>
> Yeah, it is, but it seems that the code I added in
> add_rtes_to_flat_rtable() to accumulate various subplans' relpermlists
> into finalrelpermlist didn't actually bother to unique'ify the list.
> It used list_concat() to combine finalrelpermlist and a given
> subplan's relpermlist, instead of MergeRelPemissionInfos to merge the
> two lists.
>
> I've fixed that in the attached and can now see that the plan for the
> query in your example ends up with just one RelPermissionInfo which
> combines the permissions and column bitmapsets for all operations.
>
> > 6. acesss?
> >
> > - * Set flags and access permissions.
> > + * Set flags and initialize acesss permissions.
> >
> > 7. I was expecting to see an |= here:
> >
> > + /* "merge" proprties. */
> > + dest_perminfo->inh = src_perminfo->inh;
> >
> > Why is a plain assignment ok?
>
> You're perhaps right that |= is correct.  I forget the details but I
> think I added 'inh' field to RelPemissionInfo to get some tests in
> sepgsql contrib module to pass and those tests apparently didn't mind
> the current coding.
>
> > 8. Some indentation problems here:
> >
> > @@ -3170,6 +3148,8 @@ rewriteTargetView(Query *parsetree, Relation view)
> >
> >   base_rt_index = rtr->rtindex;
> >   base_rte = rt_fetch(base_rt_index, viewquery->rtable);
> > +base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte,
> > + false);
>
> Fixed.
>
> >
> > 9. You can use foreach_current_index(lc) + 1 in:
> >
> > + i = 0;
> > + foreach(lc, relpermlist)
> > + {
> > + perminfo = (RelPermissionInfo *) lfirst(lc);
> > + if (perminfo->relid == rte->relid)
> > + {
> > + /* And set the index in RTE. */
> > + rte->perminfoindex = i + 1;
> > + return perminfo;
> > + }
> > + i++;
> > + }
>
> Oh, nice tip.  Done.
>
> > 10. I think the double quote is not in the correct place in this comment:
> >
> >   List    *finalrtable; /* "flat" rangetable for executor */
> >
> > + List    *finalrelpermlist; /* "flat list of RelPermissionInfo "*/
> >
> >
> > 11. Looks like an accidental change:
> >
> > +++ b/src/include/optimizer/planner.h
> > @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel,
> >
> >  extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr);
> >
> > +
> >
> > 12. These need to be broken into multiple lines:
> >
> > +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist,
> > RangeTblEntry *rte);
> > +extern void MergeRelPermissionInfos(Query *dest_query, List *src_relpermlist);
> > +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist,
> > RangeTblEntry *rte, bool missing_ok);
>
> All fixed.
>
> v11 attached.
>
> --
> Amit Langote
> EDB: http://www.enterprisedb.com



--
greg





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-04-05 20:21  David Rowley <dgrowleyml@gmail.com>
  parent: Greg Stark <stark@mit.edu>
  0 siblings, 1 reply; 97+ messages in thread

From: David Rowley @ 2022-04-05 20:21 UTC (permalink / raw)
  To: Greg Stark <stark@mit.edu>; +Cc: Amit Langote <amitlangote09@gmail.com>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, 6 Apr 2022 at 02:27, Greg Stark <stark@mit.edu> wrote:
>
> This is failing regression tests. I don't understand how this patch
> could be affecting this test though. Perhaps it's a problem with the
> json patches that were committed recently -- but they don't seem to be
> causing other patches to fail.

I think this will just be related to the useprefix =
list_length(es->rtable) > 1; in show_plan_tlist().  There's likely not
much point in keeping the RTE for the view anymore. IIRC it was just
there to check permissions. Amit has now added another way of doing
those.

David





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-04-06 01:18  Amit Langote <amitlangote09@gmail.com>
  parent: David Rowley <dgrowleyml@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-04-06 01:18 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Apr 6, 2022 at 5:22 AM David Rowley <dgrowleyml@gmail.com> wrote:
> On Wed, 6 Apr 2022 at 02:27, Greg Stark <stark@mit.edu> wrote:
> >
> > This is failing regression tests. I don't understand how this patch
> > could be affecting this test though. Perhaps it's a problem with the
> > json patches that were committed recently -- but they don't seem to be
> > causing other patches to fail.
>
> I think this will just be related to the useprefix =
> list_length(es->rtable) > 1; in show_plan_tlist().  There's likely not
> much point in keeping the RTE for the view anymore. IIRC it was just
> there to check permissions. Amit has now added another way of doing
> those.

That is correct.

I have rebased the patch and updated expected output of the failing test.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v12-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 121.1K) download From 59d4c00eed679b19a06a3d1799e28f52be4854e1 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v12 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 34 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/jsonb_sqljson.out | 62 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 706 +++++++++--------- src/test/regress/expected/sqljson.out | 6 +- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 28 files changed, 747 insertions(+), 826 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 11e9b4e8cc..89e9d72890 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6423,10 +6423,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6517,10 +6517,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b97b8b0435..2cbc53b42c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 f0958f03a0..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fca2a1eaf0..6225ccba30 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1859,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 75b754a420..f94d65b9e7 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2216,7 +2216,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2232,7 +2232,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2248,7 +2248,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2264,7 +2264,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2282,7 +2282,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3269,7 +3269,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 601047fa3d..36bed0ac1b 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1544,7 +1544,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1596,7 +1596,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1612,7 +1612,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1628,7 +1628,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2113,15 +2113,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 07473dd660..bc5d230e03 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 32385bbb0e..e60bdf2dff 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1604,9 +1604,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1627,9 +1627,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1719,8 +1719,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1965,7 +1965,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2017,19 +2017,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 6a56f0b09c..d3e7b23332 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/jsonb_sqljson.out b/src/test/regress/expected/jsonb_sqljson.out index ae77af7ae2..5948e765ea 100644 --- a/src/test/regress/expected/jsonb_sqljson.out +++ b/src/test/regress/expected/jsonb_sqljson.out @@ -1159,37 +1159,37 @@ SELECT * FROM ); \sv jsonb_table_view CREATE OR REPLACE VIEW public.jsonb_table_view AS - SELECT "json_table".id, - "json_table".id2, - "json_table"."int", - "json_table".text, - "json_table"."char(4)", - "json_table".bool, - "json_table"."numeric", - "json_table".domain, - "json_table".js, - "json_table".jb, - "json_table".jst, - "json_table".jsc, - "json_table".jsv, - "json_table".jsb, - "json_table".jsbq, - "json_table".aaa, - "json_table".aaa1, - "json_table".exists1, - "json_table".exists2, - "json_table".exists3, - "json_table".js2, - "json_table".jsb2w, - "json_table".jsb2q, - "json_table".ia, - "json_table".ta, - "json_table".jba, - "json_table".a1, - "json_table".b1, - "json_table".a11, - "json_table".a21, - "json_table".a22 + SELECT id, + id2, + "int", + text, + "char(4)", + bool, + "numeric", + domain, + js, + jb, + jst, + jsc, + jsv, + jsb, + jsbq, + aaa, + aaa1, + exists1, + exists2, + exists3, + js2, + jsb2w, + jsb2q, + ia, + ta, + jba, + a1, + b1, + a11, + a21, + a22 FROM JSON_TABLE( 'null'::jsonb, '$[*]' AS json_table_path_1 PASSING diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 423b9b99fb..598cf30ec9 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,13 +1419,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1451,10 +1451,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1700,23 +1700,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1730,10 +1730,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1801,13 +1801,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1820,57 +1820,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1893,8 +1893,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1903,13 +1903,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2055,26 +2055,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2093,41 +2093,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2137,68 +2137,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2215,19 +2215,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2237,19 +2237,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2293,64 +2293,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2535,24 +2535,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3072,7 +3072,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3111,8 +3111,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3124,8 +3124,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3137,8 +3137,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3150,8 +3150,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 6cadd87868..44b84a7188 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1007,7 +1007,7 @@ SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING t FROM generate_series(1,5) i; \sv json_objectagg_view CREATE OR REPLACE VIEW public.json_objectagg_view AS - SELECT JSON_OBJECTAGG(i.i : ('111'::text || i.i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.i > 3) AS "json_objectagg" + SELECT JSON_OBJECTAGG(i : ('111'::text || i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i > 3) AS "json_objectagg" FROM generate_series(1, 5) i(i) DROP VIEW json_objectagg_view; -- Test JSON_ARRAYAGG deparsing @@ -1043,7 +1043,7 @@ SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text FROM generate_series(1,5) i; \sv json_arrayagg_view CREATE OR REPLACE VIEW public.json_arrayagg_view AS - SELECT JSON_ARRAYAGG(('111'::text || i.i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.i > 3) AS "json_arrayagg" + SELECT JSON_ARRAYAGG(('111'::text || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3) AS "json_arrayagg" FROM generate_series(1, 5) i(i) DROP VIEW json_arrayagg_view; -- Test JSON_ARRAY(subquery) deparsing @@ -1261,7 +1261,7 @@ SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT \sv is_json_view CREATE OR REPLACE VIEW public.is_json_view AS SELECT '1'::text IS JSON AS "any", - ('1'::text || i.i) IS JSON SCALAR AS scalar, + ('1'::text || i) IS JSON SCALAR AS scalar, NOT '[]'::text IS JSON ARRAY AS "array", '{}'::text IS JSON OBJECT WITH UNIQUE KEYS AS object FROM generate_series(1, 3) i(i) diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index cd812336f2..b09603dc98 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index bb9ff7f07b..5e4612fff1 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7c6de7cc07..bea54c91cd 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55b65ef324..bb994a05de 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 0484260281..974b14f859 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.24.1
v12-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 158.6K) download From 4b69fee2c86e800ff9c8451d33715dec9a7fdb57 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v12 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 +++-- contrib/sepgsql/dml.c | 42 +-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 105 +++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 159 ++++++--- src/backend/nodes/copyfuncs.c | 32 +- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 +- src/backend/nodes/readfuncs.c | 21 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 128 +++---- src/backend/optimizer/plan/subselect.c | 6 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 170 +++++++--- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 60 +++- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 357 +++++++++++++++----- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 25 +- src/backend/rewrite/rewriteHandler.c | 189 +++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 89 +++-- src/include/nodes/pathnodes.h | 2 + src/include/nodes/plannodes.h | 4 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 8 + src/include/rewrite/rewriteHandler.h | 2 +- 53 files changed, 1173 insertions(+), 682 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index c51dd68722..77970b5d0f 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1934,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1942,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1966,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1978,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2136,6 +2171,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2213,6 +2249,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2229,7 +2267,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2615,7 +2654,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2635,13 +2673,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3939,12 +3976,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3956,12 +3993,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 97e61b8043..cb9aeb175d 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 689713ea58..761653f6d5 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 35a1d3a774..c8c9e87ef9 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1383,7 +1389,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index cd30f15eba..356a93f7c5 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1231,7 +1231,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7febb5018f..d0a989b1ae 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1201,7 +1201,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9626,7 +9627,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9793,7 +9795,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9999,7 +10002,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10177,7 +10181,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12271,7 +12276,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18046,7 +18052,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18973,7 +18980,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..f0958f03a0 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,7 +353,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ef2fd46092..836e5ef344 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,7 +74,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -567,38 +567,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -606,32 +607,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 9a0d5d59ef..815577e5a2 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 615bd80973..db91aaa615 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -781,7 +783,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -879,7 +882,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1141,7 +1145,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..e9cf39dafb 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d5760b1006..e4bea167cf 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -782,6 +783,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1272,6 +1274,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2944,6 +2965,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2969,12 +2991,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3693,6 +3709,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5705,6 +5722,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 1f765f42c9..b550b1f92f 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1332,6 +1332,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -3130,6 +3131,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -3155,17 +3157,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -4289,6 +4299,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index abb1f787ef..28d2814b16 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -711,6 +712,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -999,6 +1001,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2416,6 +2433,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3221,6 +3239,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3428,6 +3447,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3481,12 +3501,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -4179,6 +4193,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e7d008b2c5..f9040e2671 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1667,6 +1668,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1730,13 +1732,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_OID_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); READ_OID_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1815,6 +1828,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2302,6 +2316,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -3080,6 +3095,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 179c87c671..e9efb3edef 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4095,6 +4095,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5745,7 +5748,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b2569c5d0c..00680d5361 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5988,6 +5992,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6115,6 +6120,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index bf4c722c02..59d84a7613 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -24,6 +24,7 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -104,9 +105,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -259,7 +258,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -345,10 +344,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -356,34 +362,14 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) /* * Add the query's own RTEs to the flattened rangetable. - * - * 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. - */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -400,73 +386,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + MergeRelPermissionInfos(&glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; + /* Now merge the main query's RelPermissionInfos into the global list. */ + MergeRelPermissionInfos(&glob->finalrelpermlist, root->parse->relpermlist); - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally update the flat rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(glob->finalrtable, + glob->finalrelpermlist); } /* @@ -475,9 +417,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -488,6 +428,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 863e0e24a1..7b55f16d76 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1507,6 +1507,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subselect->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 0bd99acf83..ae36b47e58 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1211,6 +1204,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1349,6 +1348,13 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* Add the child query's RelPermissionInfos into the parent query. */ + MergeRelPermissionInfos(&root->parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(root->parse->rtable, + root->parse->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..919e2ec1e9 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..64a00b541b 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RelPermissionInfo, though + * only the tables mentioned in query are assigned RelPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 0144284aa3..dd386ac4ae 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -550,7 +551,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -669,6 +670,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +918,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +946,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1105,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1398,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1625,6 +1633,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1871,6 +1880,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2342,6 +2352,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2408,6 +2419,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2426,7 +2438,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2438,7 +2450,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2479,8 +2491,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2767,6 +2779,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3245,9 +3258,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3303,9 +3323,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index dafde68b20..ee5dba0960 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3247,16 +3247,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index 5d0035a12b..281ac6cbb4 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -209,6 +209,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) exprLocation(stmt->sourceRelation)); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; /* * Transform the join condition. This includes references to the target @@ -281,7 +282,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -340,7 +341,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -354,8 +355,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 5448cb01fa..226ba134c5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -82,6 +82,12 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, List **colnames, List **colvars); static int specialAttNum(const char *attname); static bool isQueryUsingTempRelation_walker(Node *node, void *context); +static RelPermissionInfo *AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex); +static RelPermissionInfo *GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok); +static Index GetRelPermissionInfoIndex(List *relpermlist, Oid relid); /* @@ -1021,10 +1027,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1244,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1286,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1430,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1467,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1476,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1497,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1534,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1558,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1567,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1588,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1639,21 +1658,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1946,20 +1959,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1971,7 +1977,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2018,20 +2024,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2106,19 +2105,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2197,19 +2190,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2224,6 +2211,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2347,19 +2335,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2473,16 +2455,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2494,7 +2473,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3115,6 +3094,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3132,7 +3112,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3681,3 +3664,221 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless one with the same OID is found in it + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + Assert(rte->rtekind == RTE_RELATION); + + Assert(rte->perminfoindex == 0); + return AddRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex); +} + +/* + * AddRelPermissionInfoInternal + * Sub-routine of AddRelPermissionInfo that does the actual work + */ +static RelPermissionInfo * +AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex) +{ + RelPermissionInfo *perminfo; + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfoInternal(*relpermlist, relid, perminfoindex, + true); + if (perminfo) + { + Assert(*perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Note its index. */ + *perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Find RelPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + perminfo = GetRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex, false); + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} + +/* + * GetRelPermissionInfoInternal + * Sub-routine of GetRelPermissionInfo that does the actual work + * + * If *perminfoindex is 0, the list is scanned to find one with given relid. + * If found, *perminfoindex is set to its 1-based index in the list. + * + * If *perminfoindex is already valid (> 0), it means that the caller expects + * to find the entry it's looking for at that location in the list. + */ +static RelPermissionInfo * +GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok) +{ + RelPermissionInfo *perminfo; + + if (*perminfoindex == 0) + { + ListCell *lc; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == relid) + { + *perminfoindex = foreach_current_index(lc) + 1; + return perminfo; + } + } + } + else if (*perminfoindex > 0) + { + + perminfo = (RelPermissionInfo *) + list_nth(relpermlist, *perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * GetRelPermissionInfoIndex + * Returns a 1-based index of the RelPermissionInfo of matching relid if + * found in the given list + * + * 0 indicates that one was not found. + */ +static Index +GetRelPermissionInfoIndex(List *relpermlist, Oid relid) +{ + ListCell *lc; + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(lc); + + if (perminfo->relid == relid) + return foreach_current_index(lc) + 1; + } + + return 0; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source query (src_relpermlist) + * into the destination query's list (*dest_relpermlist), "merging" + * properties of any that are present in both. + * + * Caller must subsequently call ReassignRangeTablePermInfoIndexes() on the + * source query's range table. + */ +void +MergeRelPermissionInfos(List **dest_relpermlist, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *dest_perminfo; + Index ignored = 0; + + dest_perminfo = AddRelPermissionInfoInternal(dest_relpermlist, + src_perminfo->relid, + &ignored); + + dest_perminfo->inh |= src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } +} + +/* + * ReassignRangeTablePermInfoIndexes + * Updates perminfoindex of the relation RTEs in rtable so that they reflect + * their respective RelPermissionInfo entry's current position in permlist. + * + * This is provided for the sites that use MergeRelPermissionInfos() to merge + * the RelPermissionInfo entries of two queries. + */ +void +ReassignRangeTablePermInfoIndexes(List *rtable, List *relpermlist) +{ + ListCell *l; + + foreach(l, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + /* + * Only RELATIONs would have been assigned a RelPermissionInfo and that + * too only those that are mentioned in the query (not inheritance + * child relations that are added afterwards), so we also check that + * the RTE's existing index is valid. + */ + if (rte->rtekind != RTE_RELATION && rte->perminfoindex > 0) + continue; + rte->perminfoindex = GetRelPermissionInfoIndex(relpermlist, rte->relid); + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2a1d44b813..4543949a54 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index cd946c7692..e6117823dd 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3013,9 +3014,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3071,6 +3069,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3113,8 +3112,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index f3868b3e1f..6745972d76 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -157,6 +157,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -492,6 +493,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1830,7 +1833,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1878,7 +1881,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1888,14 +1891,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 20d0b1e125..3d9ad3d8cd 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1127,7 +1127,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..9144843fd5 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -787,14 +788,7 @@ 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 rtable entries. - * - * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD - * RTE entry will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * field to the given userid in all RelPermissionInfos of the query. */ void setRuleCheckAsUser(Node *node, Oid userid) @@ -821,18 +815,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RelPermissionInfos for this query. */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 29ae27e5e3..fca2a1eaf0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,27 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(&sub_action->relpermlist, parsetree->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(sub_action->rtable, + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1594,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1623,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3174,6 +3150,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index a233dd4758..d0a292d46c 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ca48395d5c..cc68e68f1d 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 01d4c22cfc..4590e1148c 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1367,8 +1367,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1386,32 +1386,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1421,9 +1415,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index fb4fb987e7..49b1e31bc1 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5169,7 +5169,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5221,7 +5221,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5300,7 +5300,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5348,7 +5348,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5409,6 +5409,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5417,7 +5418,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5486,7 +5487,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index a15ce9edb1..982c4ce33c 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -846,8 +846,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RelPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 873772f188..222acf6e88 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -602,6 +603,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index cbbcff81d2..7b04008a12 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -548,6 +548,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -595,6 +603,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 300824258e..11c495e800 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -91,6 +91,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 4a2ca81f3c..91559c1f41 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -147,6 +147,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -953,37 +955,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1037,11 +1008,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1161,14 +1138,60 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table, though + * different RTEs for the same relation share the RelPermissionInfo, that + * is, there is only one RelPermissionInfo containing a given relid. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 6cbcb67bdf..524266fb6f 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat" list of RelPermissionInfo */ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 50ef3dda05..66fb82ab01 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -66,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -654,6 +657,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index cf9c759025..e573b1620f 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -181,6 +181,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +236,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -268,6 +271,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index de21c3c649..923add1176 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,13 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, + RangeTblEntry *rte); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, + RangeTblEntry *rte); +extern void MergeRelPermissionInfos(List **dest_relpermlist, + List *src_relpermlist); +extern void ReassignRangeTablePermInfoIndexes(List *rtable, + List *relpermlist); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-04-11 05:41  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-04-11 05:41 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Rebased to keep the cfbot green for now.

-- 
Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v13-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 158.6K) download From 13fb776cf80fadbee8d8209749327b39e6c3c4c3 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v13 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 +++-- contrib/sepgsql/dml.c | 42 +-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 105 +++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 159 ++++++--- src/backend/nodes/copyfuncs.c | 32 +- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 +- src/backend/nodes/readfuncs.c | 21 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 128 +++---- src/backend/optimizer/plan/subselect.c | 6 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 170 +++++++--- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 60 +++- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 357 +++++++++++++++----- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 25 +- src/backend/rewrite/rewriteHandler.c | 189 +++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 89 +++-- src/include/nodes/pathnodes.h | 2 + src/include/nodes/plannodes.h | 4 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 8 + src/include/rewrite/rewriteHandler.h | 2 +- 53 files changed, 1173 insertions(+), 682 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index c51dd68722..77970b5d0f 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1934,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1942,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1966,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1978,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2136,6 +2171,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2213,6 +2249,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2229,7 +2267,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2615,7 +2654,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2635,13 +2673,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3939,12 +3976,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3956,12 +3993,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 97e61b8043..cb9aeb175d 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 689713ea58..761653f6d5 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 35a1d3a774..c8c9e87ef9 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1383,7 +1389,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index cd30f15eba..356a93f7c5 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1231,7 +1231,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 90edd0bb97..12b8c3e52a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1201,7 +1201,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9651,7 +9652,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9818,7 +9820,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10024,7 +10027,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10202,7 +10206,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12296,7 +12301,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18048,7 +18054,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18975,7 +18982,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..f0958f03a0 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,7 +353,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ef2fd46092..836e5ef344 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,7 +74,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -567,38 +567,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -606,32 +607,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 9a0d5d59ef..815577e5a2 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 615bd80973..db91aaa615 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -781,7 +783,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -879,7 +882,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1141,7 +1145,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..e9cf39dafb 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 836f427ea8..bdf8cb2aa8 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -783,6 +784,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1276,6 +1278,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2948,6 +2969,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2973,12 +2995,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3698,6 +3714,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5710,6 +5727,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index e013c1bbfe..8863f044ae 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1332,6 +1332,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -3130,6 +3131,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -3155,17 +3157,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -4290,6 +4300,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index d5f5e76c55..38183616e6 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -315,6 +315,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -712,6 +713,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -1003,6 +1005,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2422,6 +2439,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3227,6 +3245,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3435,6 +3454,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3488,12 +3508,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -4186,6 +4200,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 3d150cb25d..7d05146d38 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1668,6 +1669,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1731,13 +1733,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_OID_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); READ_OID_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1816,6 +1829,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2304,6 +2318,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -3085,6 +3100,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 95476ada0b..e017dd34fc 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4129,6 +4129,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5780,7 +5783,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b090b087e9..b70778d9fa 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6001,6 +6005,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6128,6 +6133,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 6ea3505646..70da36e390 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -24,6 +24,7 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -111,9 +112,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -268,7 +267,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -354,10 +353,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -365,34 +371,14 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) /* * Add the query's own RTEs to the flattened rangetable. - * - * 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. - */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -409,73 +395,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + MergeRelPermissionInfos(&glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; + /* Now merge the main query's RelPermissionInfos into the global list. */ + MergeRelPermissionInfos(&glob->finalrelpermlist, root->parse->relpermlist); - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally update the flat rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(glob->finalrtable, + glob->finalrelpermlist); } /* @@ -484,9 +426,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -497,6 +437,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 863e0e24a1..7b55f16d76 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1507,6 +1507,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subselect->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 0bd99acf83..ae36b47e58 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1211,6 +1204,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1349,6 +1348,13 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* Add the child query's RelPermissionInfos into the parent query. */ + MergeRelPermissionInfos(&root->parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(root->parse->rtable, + root->parse->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..919e2ec1e9 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..64a00b541b 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RelPermissionInfo, though + * only the tables mentioned in query are assigned RelPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 0144284aa3..dd386ac4ae 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -550,7 +551,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -669,6 +670,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +918,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +946,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1105,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1398,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1625,6 +1633,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1871,6 +1880,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2342,6 +2352,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2408,6 +2419,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2426,7 +2438,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2438,7 +2450,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2479,8 +2491,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2767,6 +2779,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3245,9 +3258,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3303,9 +3323,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index dafde68b20..ee5dba0960 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3247,16 +3247,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index 5d0035a12b..281ac6cbb4 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -209,6 +209,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) exprLocation(stmt->sourceRelation)); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; /* * Transform the join condition. This includes references to the target @@ -281,7 +282,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -340,7 +341,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -354,8 +355,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 5448cb01fa..226ba134c5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -82,6 +82,12 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, List **colnames, List **colvars); static int specialAttNum(const char *attname); static bool isQueryUsingTempRelation_walker(Node *node, void *context); +static RelPermissionInfo *AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex); +static RelPermissionInfo *GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok); +static Index GetRelPermissionInfoIndex(List *relpermlist, Oid relid); /* @@ -1021,10 +1027,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1244,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1286,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1430,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1467,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1476,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1497,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1534,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1558,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1567,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1588,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1639,21 +1658,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1946,20 +1959,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1971,7 +1977,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2018,20 +2024,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2106,19 +2105,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2197,19 +2190,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2224,6 +2211,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2347,19 +2335,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2473,16 +2455,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2494,7 +2473,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3115,6 +3094,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3132,7 +3112,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3681,3 +3664,221 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless one with the same OID is found in it + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + Assert(rte->rtekind == RTE_RELATION); + + Assert(rte->perminfoindex == 0); + return AddRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex); +} + +/* + * AddRelPermissionInfoInternal + * Sub-routine of AddRelPermissionInfo that does the actual work + */ +static RelPermissionInfo * +AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex) +{ + RelPermissionInfo *perminfo; + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfoInternal(*relpermlist, relid, perminfoindex, + true); + if (perminfo) + { + Assert(*perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Note its index. */ + *perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Find RelPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + perminfo = GetRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex, false); + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} + +/* + * GetRelPermissionInfoInternal + * Sub-routine of GetRelPermissionInfo that does the actual work + * + * If *perminfoindex is 0, the list is scanned to find one with given relid. + * If found, *perminfoindex is set to its 1-based index in the list. + * + * If *perminfoindex is already valid (> 0), it means that the caller expects + * to find the entry it's looking for at that location in the list. + */ +static RelPermissionInfo * +GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok) +{ + RelPermissionInfo *perminfo; + + if (*perminfoindex == 0) + { + ListCell *lc; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == relid) + { + *perminfoindex = foreach_current_index(lc) + 1; + return perminfo; + } + } + } + else if (*perminfoindex > 0) + { + + perminfo = (RelPermissionInfo *) + list_nth(relpermlist, *perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * GetRelPermissionInfoIndex + * Returns a 1-based index of the RelPermissionInfo of matching relid if + * found in the given list + * + * 0 indicates that one was not found. + */ +static Index +GetRelPermissionInfoIndex(List *relpermlist, Oid relid) +{ + ListCell *lc; + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(lc); + + if (perminfo->relid == relid) + return foreach_current_index(lc) + 1; + } + + return 0; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source query (src_relpermlist) + * into the destination query's list (*dest_relpermlist), "merging" + * properties of any that are present in both. + * + * Caller must subsequently call ReassignRangeTablePermInfoIndexes() on the + * source query's range table. + */ +void +MergeRelPermissionInfos(List **dest_relpermlist, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *dest_perminfo; + Index ignored = 0; + + dest_perminfo = AddRelPermissionInfoInternal(dest_relpermlist, + src_perminfo->relid, + &ignored); + + dest_perminfo->inh |= src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } +} + +/* + * ReassignRangeTablePermInfoIndexes + * Updates perminfoindex of the relation RTEs in rtable so that they reflect + * their respective RelPermissionInfo entry's current position in permlist. + * + * This is provided for the sites that use MergeRelPermissionInfos() to merge + * the RelPermissionInfo entries of two queries. + */ +void +ReassignRangeTablePermInfoIndexes(List *rtable, List *relpermlist) +{ + ListCell *l; + + foreach(l, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + /* + * Only RELATIONs would have been assigned a RelPermissionInfo and that + * too only those that are mentioned in the query (not inheritance + * child relations that are added afterwards), so we also check that + * the RTE's existing index is valid. + */ + if (rte->rtekind != RTE_RELATION && rte->perminfoindex > 0) + continue; + rte->perminfoindex = GetRelPermissionInfoIndex(relpermlist, rte->relid); + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2a1d44b813..4543949a54 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 2826559d09..5ca08dc6a1 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1222,7 +1222,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3014,9 +3015,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3072,6 +3070,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3114,8 +3113,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 9181d3e863..6c327f3573 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -491,6 +492,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1778,7 +1781,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1826,7 +1829,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1836,14 +1839,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index fe5accca57..5b16b78bb2 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1108,7 +1108,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..9144843fd5 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -787,14 +788,7 @@ 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 rtable entries. - * - * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD - * RTE entry will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * field to the given userid in all RelPermissionInfos of the query. */ void setRuleCheckAsUser(Node *node, Oid userid) @@ -821,18 +815,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RelPermissionInfos for this query. */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 29ae27e5e3..fca2a1eaf0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,27 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(&sub_action->relpermlist, parsetree->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(sub_action->rtable, + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1594,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1623,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3174,6 +3150,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index a233dd4758..d0a292d46c 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ca48395d5c..cc68e68f1d 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 01d4c22cfc..4590e1148c 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1367,8 +1367,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1386,32 +1386,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1421,9 +1415,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index fb4fb987e7..49b1e31bc1 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5169,7 +5169,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5221,7 +5221,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5300,7 +5300,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5348,7 +5348,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5409,6 +5409,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5417,7 +5418,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5486,7 +5487,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 43f14c233d..5610ada989 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -846,8 +846,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RelPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 873772f188..222acf6e88 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -602,6 +603,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 94b191f8ae..4dc29324cd 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -548,6 +548,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -595,6 +603,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 340d28f4e1..282c9ebb28 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -91,6 +91,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index da02658c81..cf8a28ed8f 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -147,6 +147,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -953,37 +955,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1037,11 +1008,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1161,14 +1138,60 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table, though + * different RTEs for the same relation share the RelPermissionInfo, that + * is, there is only one RelPermissionInfo containing a given relid. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index c5ab53e05c..eec7975afc 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat" list of RelPermissionInfo */ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index e43e360d9b..5832905e22 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -66,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -666,6 +669,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index cf9c759025..e573b1620f 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -181,6 +181,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +236,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -268,6 +271,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index de21c3c649..923add1176 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,13 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, + RangeTblEntry *rte); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, + RangeTblEntry *rte); +extern void MergeRelPermissionInfos(List **dest_relpermlist, + List *src_relpermlist); +extern void ReassignRangeTablePermInfoIndexes(List *rtable, + List *relpermlist); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1
v13-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 122.1K) download From b1c97ab0ab1b74f11a96e7705e1e0316844e27a8 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v13 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 34 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/jsonb_sqljson.out | 62 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 726 +++++++++--------- src/test/regress/expected/sqljson.out | 6 +- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 28 files changed, 757 insertions(+), 836 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 30e95f585f..472cd776e2 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6423,10 +6423,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6517,10 +6517,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b97b8b0435..2cbc53b42c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 f0958f03a0..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fca2a1eaf0..6225ccba30 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1859,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index c65c92bfb0..6fe8e61ab2 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2216,7 +2216,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2232,7 +2232,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2248,7 +2248,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2264,7 +2264,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2282,7 +2282,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3237,7 +3237,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 601047fa3d..36bed0ac1b 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1544,7 +1544,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1596,7 +1596,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1612,7 +1612,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1628,7 +1628,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2113,15 +2113,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 5ede56d9b5..7315ddb92a 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 32385bbb0e..e60bdf2dff 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1604,9 +1604,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1627,9 +1627,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1719,8 +1719,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1965,7 +1965,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2017,19 +2017,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index 6a56f0b09c..d3e7b23332 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -506,16 +506,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/jsonb_sqljson.out b/src/test/regress/expected/jsonb_sqljson.out index 28338b4d19..0211e9d282 100644 --- a/src/test/regress/expected/jsonb_sqljson.out +++ b/src/test/regress/expected/jsonb_sqljson.out @@ -1159,37 +1159,37 @@ SELECT * FROM ); \sv jsonb_table_view CREATE OR REPLACE VIEW public.jsonb_table_view AS - SELECT "json_table".id, - "json_table".id2, - "json_table"."int", - "json_table".text, - "json_table"."char(4)", - "json_table".bool, - "json_table"."numeric", - "json_table".domain, - "json_table".js, - "json_table".jb, - "json_table".jst, - "json_table".jsc, - "json_table".jsv, - "json_table".jsb, - "json_table".jsbq, - "json_table".aaa, - "json_table".aaa1, - "json_table".exists1, - "json_table".exists2, - "json_table".exists3, - "json_table".js2, - "json_table".jsb2w, - "json_table".jsb2q, - "json_table".ia, - "json_table".ta, - "json_table".jba, - "json_table".a1, - "json_table".b1, - "json_table".a11, - "json_table".a21, - "json_table".a22 + SELECT id, + id2, + "int", + text, + "char(4)", + bool, + "numeric", + domain, + js, + jb, + jst, + jsc, + jsv, + jsb, + jsbq, + aaa, + aaa1, + exists1, + exists2, + exists3, + js2, + jsb2w, + jsb2q, + ia, + ta, + jba, + a1, + b1, + a11, + a21, + a22 FROM JSON_TABLE( 'null'::jsonb, '$[*]' AS json_table_path_1 PASSING diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 313c72a268..03d2de7d3a 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 21effe8315..30bb11ba3a 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,13 +1419,13 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1443,10 +1443,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1692,23 +1692,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1722,10 +1722,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1793,13 +1793,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1812,57 +1812,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1885,8 +1885,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1895,13 +1895,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2011,16 +2011,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2058,26 +2058,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2096,41 +2096,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2140,68 +2140,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2218,19 +2218,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2240,19 +2240,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2296,64 +2296,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2538,24 +2538,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3075,7 +3075,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3114,8 +3114,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3127,8 +3127,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3140,8 +3140,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3153,8 +3153,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 6cadd87868..44b84a7188 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1007,7 +1007,7 @@ SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING t FROM generate_series(1,5) i; \sv json_objectagg_view CREATE OR REPLACE VIEW public.json_objectagg_view AS - SELECT JSON_OBJECTAGG(i.i : ('111'::text || i.i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.i > 3) AS "json_objectagg" + SELECT JSON_OBJECTAGG(i : ('111'::text || i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i > 3) AS "json_objectagg" FROM generate_series(1, 5) i(i) DROP VIEW json_objectagg_view; -- Test JSON_ARRAYAGG deparsing @@ -1043,7 +1043,7 @@ SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text FROM generate_series(1,5) i; \sv json_arrayagg_view CREATE OR REPLACE VIEW public.json_arrayagg_view AS - SELECT JSON_ARRAYAGG(('111'::text || i.i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.i > 3) AS "json_arrayagg" + SELECT JSON_ARRAYAGG(('111'::text || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3) AS "json_arrayagg" FROM generate_series(1, 5) i(i) DROP VIEW json_arrayagg_view; -- Test JSON_ARRAY(subquery) deparsing @@ -1261,7 +1261,7 @@ SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT \sv is_json_view CREATE OR REPLACE VIEW public.is_json_view AS SELECT '1'::text IS JSON AS "any", - ('1'::text || i.i) IS JSON SCALAR AS scalar, + ('1'::text || i) IS JSON SCALAR AS scalar, NOT '[]'::text IS JSON ARRAY AS "array", '{}'::text IS JSON OBJECT WITH UNIQUE KEYS AS object FROM generate_series(1, 3) i(i) diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index cd812336f2..b09603dc98 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index d78b4c463c..b291f36ecf 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7c6de7cc07..bea54c91cd 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -872,9 +872,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1394,9 +1394,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1416,9 +1416,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55b65ef324..bb994a05de 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 0484260281..974b14f859 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.24.1
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-07-06 03:25  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-07-06 03:25 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Apr 11, 2022 at 2:41 PM Amit Langote <amitlangote09@gmail.com> wrote:
> Rebased to keep the cfbot green for now.

And again to fix the rules.out conflicts.

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v14-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 158.6K) download From 2dda5b1cf12f8803abd77f86dc43852dc7e39fa1 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v14 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 +++-- contrib/sepgsql/dml.c | 42 +-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 105 +++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 159 ++++++--- src/backend/nodes/copyfuncs.c | 32 +- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/outfuncs.c | 29 +- src/backend/nodes/readfuncs.c | 21 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 128 +++---- src/backend/optimizer/plan/subselect.c | 6 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 170 +++++++--- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 60 +++- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 357 +++++++++++++++----- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 25 +- src/backend/rewrite/rewriteHandler.c | 189 +++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 89 +++-- src/include/nodes/pathnodes.h | 2 + src/include/nodes/plannodes.h | 4 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 8 + src/include/rewrite/rewriteHandler.h | 2 +- 53 files changed, 1173 insertions(+), 682 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 955a428e3d..b932d3a5ed 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1934,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1942,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1966,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1978,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2145,6 +2180,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2222,6 +2258,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2238,7 +2276,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2624,7 +2663,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2644,13 +2682,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3945,12 +3982,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3962,12 +3999,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 97e61b8043..cb9aeb175d 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index e2870e3c11..10c1d3c2b4 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 35a1d3a774..c8c9e87ef9 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1383,7 +1389,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 99f5ab83c3..bad38f0c95 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1279,7 +1279,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 2de0ebacec..53c2978738 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1200,7 +1200,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9650,7 +9651,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9817,7 +9819,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10023,7 +10026,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10201,7 +10205,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12295,7 +12300,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18047,7 +18053,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18974,7 +18981,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..f0958f03a0 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,7 +353,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ef2fd46092..836e5ef344 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,7 +74,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -567,38 +567,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -606,32 +607,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f1fd7f7e8b..a89ab74a7e 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index e03ea27299..85a1dc84a6 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -781,7 +783,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -879,7 +882,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1141,7 +1145,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..e9cf39dafb 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 706d283a92..5d3b675f9b 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -97,6 +97,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -783,6 +784,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1276,6 +1278,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2950,6 +2971,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(relkind); COPY_SCALAR_FIELD(rellockmode); COPY_NODE_FIELD(tablesample); + COPY_SCALAR_FIELD(perminfoindex); COPY_NODE_FIELD(subquery); COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); @@ -2975,12 +2997,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3700,6 +3716,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5712,6 +5729,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index fccc0b4a18..287dbca5a4 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1350,6 +1350,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -3147,6 +3148,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(relkind); COMPARE_SCALAR_FIELD(rellockmode); COMPARE_NODE_FIELD(tablesample); + COMPARE_SCALAR_FIELD(perminfoindex); COMPARE_NODE_FIELD(subquery); COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); @@ -3172,17 +3174,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -4307,6 +4317,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 4315c53080..4cd0741549 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -326,6 +326,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -723,6 +724,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -1014,6 +1016,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2435,6 +2452,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3240,6 +3258,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3448,6 +3467,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -3501,12 +3521,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -4199,6 +4213,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 6a05b69415..6ef93cce62 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -264,6 +264,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1670,6 +1671,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -1733,13 +1735,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_OID_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); READ_OID_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1818,6 +1831,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2306,6 +2320,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -3087,6 +3102,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RANGETBLENTRY", 13)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 76606faa3e..8d60b2369d 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4149,6 +4149,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5800,7 +5803,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 06ad856eac..bad257655c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5998,6 +6002,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6125,6 +6130,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 9cef92cab2..ceedfe3cc9 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -24,6 +24,7 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -111,9 +112,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -268,7 +267,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -354,10 +353,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down any subquery + * RTEs of the current plan level that did not make into the plan tree by way + * of being pulled up, nor turned into SubqueryScan nodes referenced in the + * plan tree. Such subqueries would not thus have had any RelPermissionInfos + * referenced in them merged into root->parse->relpermlist, so we must force- + * add them to glob->finalrelpermlist. Failing to do so would prevent the + * executor from performing expected permission checks for tables mentioned in + * such subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -365,34 +371,14 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) /* * Add the query's own RTEs to the flattened rangetable. - * - * 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. - */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -409,73 +395,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + MergeRelPermissionInfos(&glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} - -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; + /* Now merge the main query's RelPermissionInfos into the global list. */ + MergeRelPermissionInfos(&glob->finalrelpermlist, root->parse->relpermlist); - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally update the flat rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(glob->finalrtable, + glob->finalrelpermlist); } /* @@ -484,9 +426,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) @@ -497,6 +437,14 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); memcpy(newrte, rte, sizeof(RangeTblEntry)); + /* + * Executor may need to look up RelPermissionInfos, so must keep + * perminfoindex around. Though, the list containing the RelPermissionInfo + * to which the index points will be folded into glob->finalrelpermlist, so + * offset the index likewise. + */ + newrte->perminfoindex += list_length(glob->finalrelpermlist); + /* zap unneeded sub-structure */ newrte->tablesample = NULL; newrte->subquery = NULL; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index df4ca12919..79a7a04032 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1499,6 +1499,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subselect->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 0bd99acf83..ae36b47e58 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1211,6 +1204,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1349,6 +1348,13 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* Add the child query's RelPermissionInfos into the parent query. */ + MergeRelPermissionInfos(&root->parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(root->parse->rtable, + root->parse->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..919e2ec1e9 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..64a00b541b 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RelPermissionInfo, though + * only the tables mentioned in query are assigned RelPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 1bcb875507..3df097b2f1 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -550,7 +551,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -669,6 +670,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +918,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +946,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1105,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1398,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1627,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1874,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2349,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2416,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2435,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2447,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2488,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2776,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3255,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3300,9 +3320,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index c655d188c7..cf1cb81586 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3233,16 +3233,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..8d920eeb7e 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 926dcbf30e..f9bcc8596d 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -82,6 +82,12 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, List **colnames, List **colvars); static int specialAttNum(const char *attname); static bool isQueryUsingTempRelation_walker(Node *node, void *context); +static RelPermissionInfo *AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex); +static RelPermissionInfo *GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok); +static Index GetRelPermissionInfoIndex(List *relpermlist, Oid relid); /* @@ -1021,10 +1027,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1244,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1286,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1430,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1467,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1476,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1497,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1534,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1558,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1567,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1588,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1639,21 +1658,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1946,20 +1959,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1971,7 +1977,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2027,20 +2033,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2115,19 +2114,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,19 +2205,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2239,6 +2226,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2362,19 +2350,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2488,16 +2470,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2509,7 +2488,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3130,6 +3109,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3147,7 +3127,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3696,3 +3679,221 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless one with the same OID is found in it + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + Assert(rte->rtekind == RTE_RELATION); + + Assert(rte->perminfoindex == 0); + return AddRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex); +} + +/* + * AddRelPermissionInfoInternal + * Sub-routine of AddRelPermissionInfo that does the actual work + */ +static RelPermissionInfo * +AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex) +{ + RelPermissionInfo *perminfo; + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfoInternal(*relpermlist, relid, perminfoindex, + true); + if (perminfo) + { + Assert(*perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Note its index. */ + *perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Find RelPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + perminfo = GetRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex, false); + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} + +/* + * GetRelPermissionInfoInternal + * Sub-routine of GetRelPermissionInfo that does the actual work + * + * If *perminfoindex is 0, the list is scanned to find one with given relid. + * If found, *perminfoindex is set to its 1-based index in the list. + * + * If *perminfoindex is already valid (> 0), it means that the caller expects + * to find the entry it's looking for at that location in the list. + */ +static RelPermissionInfo * +GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok) +{ + RelPermissionInfo *perminfo; + + if (*perminfoindex == 0) + { + ListCell *lc; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == relid) + { + *perminfoindex = foreach_current_index(lc) + 1; + return perminfo; + } + } + } + else if (*perminfoindex > 0) + { + + perminfo = (RelPermissionInfo *) + list_nth(relpermlist, *perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * GetRelPermissionInfoIndex + * Returns a 1-based index of the RelPermissionInfo of matching relid if + * found in the given list + * + * 0 indicates that one was not found. + */ +static Index +GetRelPermissionInfoIndex(List *relpermlist, Oid relid) +{ + ListCell *lc; + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(lc); + + if (perminfo->relid == relid) + return foreach_current_index(lc) + 1; + } + + return 0; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source query (src_relpermlist) + * into the destination query's list (*dest_relpermlist), "merging" + * properties of any that are present in both. + * + * Caller must subsequently call ReassignRangeTablePermInfoIndexes() on the + * source query's range table. + */ +void +MergeRelPermissionInfos(List **dest_relpermlist, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *dest_perminfo; + Index ignored = 0; + + dest_perminfo = AddRelPermissionInfoInternal(dest_relpermlist, + src_perminfo->relid, + &ignored); + + dest_perminfo->inh |= src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } +} + +/* + * ReassignRangeTablePermInfoIndexes + * Updates perminfoindex of the relation RTEs in rtable so that they reflect + * their respective RelPermissionInfo entry's current position in permlist. + * + * This is provided for the sites that use MergeRelPermissionInfos() to merge + * the RelPermissionInfo entries of two queries. + */ +void +ReassignRangeTablePermInfoIndexes(List *rtable, List *relpermlist) +{ + ListCell *l; + + foreach(l, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + /* + * Only RELATIONs would have been assigned a RelPermissionInfo and that + * too only those that are mentioned in the query (not inheritance + * child relations that are added afterwards), so we also check that + * the RTE's existing index is valid. + */ + if (rte->rtekind != RTE_RELATION && rte->perminfoindex > 0) + continue; + rte->perminfoindex = GetRelPermissionInfoIndex(relpermlist, rte->relid); + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2a1d44b813..4543949a54 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index f889726a28..fb597eb650 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1222,7 +1222,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3012,9 +3013,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3070,6 +3068,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3112,8 +3111,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 38e3b1c1b3..bda16358e6 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -491,6 +492,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1787,7 +1790,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1835,7 +1838,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1845,14 +1848,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2cbca4a087..3858186e56 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1110,7 +1110,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..9144843fd5 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -787,14 +788,7 @@ 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 rtable entries. - * - * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD - * RTE entry will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * field to the given userid in all RelPermissionInfos of the query. */ void setRuleCheckAsUser(Node *node, Oid userid) @@ -821,18 +815,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RelPermissionInfos for this query. */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 29ae27e5e3..fca2a1eaf0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,27 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(&sub_action->relpermlist, parsetree->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(sub_action->rtable, + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1594,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1623,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3174,6 +3150,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index a233dd4758..d0a292d46c 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 7c02fb279f..3ff53bd159 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 51b3fdc9a0..58d35318c1 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1374,8 +1374,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1393,32 +1393,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1428,9 +1422,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index fa1f589fad..c0914bf4a1 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5168,7 +5168,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5220,7 +5220,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5299,7 +5299,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5347,7 +5347,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5408,6 +5408,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5416,7 +5417,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5485,7 +5486,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index f502df91dc..fe2b72a851 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -846,8 +846,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RelPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index d68a6b9d28..eb812b3308 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -602,6 +603,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 5728801379..95b22bf6af 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -548,6 +548,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -595,6 +603,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 7ce1fc4deb..ff6d0a8f97 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -91,6 +91,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f93d866548..883d1fa81a 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -148,6 +148,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -956,37 +958,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1040,11 +1011,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1164,14 +1141,60 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table, though + * different RTEs for the same relation share the RelPermissionInfo, that + * is, there is only one RelPermissionInfo containing a given relid. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index b88cfb8dc0..fe03764fbb 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat" list of RelPermissionInfo */ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index d5c0ebe859..f1d677ce08 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -66,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -691,6 +694,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index cf9c759025..e573b1620f 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -181,6 +181,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +236,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -268,6 +271,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index de21c3c649..923add1176 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,13 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, + RangeTblEntry *rte); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, + RangeTblEntry *rte); +extern void MergeRelPermissionInfos(List **dest_relpermlist, + List *src_relpermlist); +extern void ReassignRangeTablePermInfoIndexes(List *rtable, + List *relpermlist); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.35.3
v14-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 122.2K) download From 7add63949eaf734a3a554c7fc93f7af5fb4591bb Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v14 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 34 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/jsonb_sqljson.out | 62 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/sqljson.out | 6 +- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 28 files changed, 758 insertions(+), 837 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 44457f930c..5913d4cee5 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6423,10 +6423,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6517,10 +6517,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b97b8b0435..2cbc53b42c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 f0958f03a0..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fca2a1eaf0..6225ccba30 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1859,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 1f08716f69..f626e59c5d 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2216,7 +2216,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2232,7 +2232,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2248,7 +2248,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2264,7 +2264,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2282,7 +2282,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3239,7 +3239,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 601047fa3d..36bed0ac1b 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1544,7 +1544,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1596,7 +1596,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1612,7 +1612,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1628,7 +1628,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2113,15 +2113,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 5ede56d9b5..7315ddb92a 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 32385bbb0e..e60bdf2dff 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1604,9 +1604,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1627,9 +1627,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1719,8 +1719,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1965,7 +1965,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2017,19 +2017,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/jsonb_sqljson.out b/src/test/regress/expected/jsonb_sqljson.out index e2f7df50a8..d157e8efdc 100644 --- a/src/test/regress/expected/jsonb_sqljson.out +++ b/src/test/regress/expected/jsonb_sqljson.out @@ -1161,37 +1161,37 @@ SELECT * FROM ); \sv jsonb_table_view CREATE OR REPLACE VIEW public.jsonb_table_view AS - SELECT "json_table".id, - "json_table".id2, - "json_table"."int", - "json_table".text, - "json_table"."char(4)", - "json_table".bool, - "json_table"."numeric", - "json_table".domain, - "json_table".js, - "json_table".jb, - "json_table".jst, - "json_table".jsc, - "json_table".jsv, - "json_table".jsb, - "json_table".jsbq, - "json_table".aaa, - "json_table".aaa1, - "json_table".exists1, - "json_table".exists2, - "json_table".exists3, - "json_table".js2, - "json_table".jsb2w, - "json_table".jsb2q, - "json_table".ia, - "json_table".ta, - "json_table".jba, - "json_table".a1, - "json_table".b1, - "json_table".a11, - "json_table".a21, - "json_table".a22 + SELECT id, + id2, + "int", + text, + "char(4)", + bool, + "numeric", + domain, + js, + jb, + jst, + jsc, + jsv, + jsb, + jsbq, + aaa, + aaa1, + exists1, + exists2, + exists3, + js2, + jsb2w, + jsb2q, + ia, + ta, + jba, + a1, + b1, + a11, + a21, + a22 FROM JSON_TABLE( 'null'::jsonb, '$[*]' AS json_table_path_1 PASSING diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 7ec3d2688f..fc35d8a567 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1453,10 +1453,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1702,23 +1702,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1732,10 +1732,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1803,13 +1803,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1822,57 +1822,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1895,8 +1895,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1905,13 +1905,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2021,16 +2021,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2068,26 +2068,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2106,41 +2106,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2150,68 +2150,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2228,19 +2228,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2250,19 +2250,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2306,64 +2306,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2548,24 +2548,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3085,7 +3085,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3124,8 +3124,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3137,8 +3137,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3150,8 +3150,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3163,8 +3163,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 0883261535..52c71d7ad7 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1049,7 +1049,7 @@ SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING t FROM generate_series(1,5) i; \sv json_objectagg_view CREATE OR REPLACE VIEW public.json_objectagg_view AS - SELECT JSON_OBJECTAGG(i.i : ('111'::text || i.i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.i > 3) AS "json_objectagg" + SELECT JSON_OBJECTAGG(i : ('111'::text || i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i > 3) AS "json_objectagg" FROM generate_series(1, 5) i(i) DROP VIEW json_objectagg_view; -- Test JSON_ARRAYAGG deparsing @@ -1085,7 +1085,7 @@ SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text FROM generate_series(1,5) i; \sv json_arrayagg_view CREATE OR REPLACE VIEW public.json_arrayagg_view AS - SELECT JSON_ARRAYAGG(('111'::text || i.i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.i > 3) AS "json_arrayagg" + SELECT JSON_ARRAYAGG(('111'::text || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3) AS "json_arrayagg" FROM generate_series(1, 5) i(i) DROP VIEW json_arrayagg_view; -- Test JSON_ARRAY(subquery) deparsing @@ -1303,7 +1303,7 @@ SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT \sv is_json_view CREATE OR REPLACE VIEW public.is_json_view AS SELECT '1'::text IS JSON AS "any", - ('1'::text || i.i) IS JSON SCALAR AS scalar, + ('1'::text || i) IS JSON SCALAR AS scalar, NOT '[]'::text IS JSON ARRAY AS "array", '{}'::text IS JSON OBJECT WITH UNIQUE KEYS AS object FROM generate_series(1, 3) i(i) diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index cd812336f2..b09603dc98 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 433a0bb025..7a9968afe2 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 30dd900e11..adbe32c32e 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55ac49be26..73a3a52562 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 493c6186e1..b524e1665f 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-07-13 08:00  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-07-13 08:00 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Rebased over 964d01ae90.


--
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v15-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 150.2K) download From c76ce37c8d19be1dff76e6f7f988be7085abf075 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v15 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 +++-- contrib/sepgsql/dml.c | 42 +-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 105 +++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 159 ++++++--- src/backend/nodes/outfuncs.c | 8 +- src/backend/nodes/readfuncs.c | 8 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 ++----- src/backend/optimizer/plan/subselect.c | 6 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 170 +++++++--- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 60 +++- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 357 +++++++++++++++----- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 25 +- src/backend/rewrite/rewriteHandler.c | 189 +++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 90 +++-- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 4 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 8 + src/include/rewrite/rewriteHandler.h | 2 +- 50 files changed, 1089 insertions(+), 679 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 955a428e3d..b932d3a5ed 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1934,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1942,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1966,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1978,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2145,6 +2180,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2222,6 +2258,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2238,7 +2276,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2624,7 +2663,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2644,13 +2682,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3945,12 +3982,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3962,12 +3999,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 97e61b8043..cb9aeb175d 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 3ac731803b..b56b7b4bda 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index a976008b3d..3407486bb3 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1383,7 +1389,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index ff847579f3..a88bf410cb 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1279,7 +1279,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ef5b34a312..f05f26e5f0 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1200,7 +1200,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9651,7 +9652,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9818,7 +9820,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10024,7 +10027,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10202,7 +10206,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12296,7 +12301,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18048,7 +18054,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18975,7 +18982,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..f0958f03a0 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,7 +353,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ef2fd46092..836e5ef344 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,7 +74,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -567,38 +567,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -606,32 +607,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f1fd7f7e8b..a89ab74a7e 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index e03ea27299..85a1dc84a6 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -781,7 +783,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -879,7 +882,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1141,7 +1145,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..e9cf39dafb 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 4d776e7b51..fe91161774 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -463,6 +463,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -505,6 +506,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -558,12 +560,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 1421686938..7f2bd79229 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -263,6 +263,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -352,6 +353,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -415,12 +417,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); - READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index e37f2933eb..a4a6b3986b 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5799,7 +5802,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 06ad856eac..bad257655c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5998,6 +6002,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6125,6 +6130,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 9cef92cab2..76d925c4ce 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -24,6 +24,7 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -111,9 +112,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -268,7 +267,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -354,10 +353,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down subquery RTEs + * of the current plan level whose query was not pulled up into the parent + * query, nor turned into SubqueryScan nodes referenced in the plan tree. Such + * subqueries would not thus have had their RelPermissionInfos merged into + * root->parse->relpermlist, so we must force-add them to + * glob->finalrelpermlist. Failing to do so would prevent the executor from + * performing expected permission checks for tables mentioned in such + * subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -365,34 +371,14 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) /* * Add the query's own RTEs to the flattened rangetable. - * - * 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. - */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -409,73 +395,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + MergeRelPermissionInfos(&glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; + /* Now merge the main query's RelPermissionInfos into the global list. */ + MergeRelPermissionInfos(&glob->finalrelpermlist, root->parse->relpermlist); - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally update the flat rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(glob->finalrtable, + glob->finalrelpermlist); } /* @@ -483,10 +425,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to check the relation's permissions. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index df4ca12919..79a7a04032 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1499,6 +1499,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subselect->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 0bd99acf83..ae36b47e58 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1211,6 +1204,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1349,6 +1348,13 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* Add the child query's RelPermissionInfos into the parent query. */ + MergeRelPermissionInfos(&root->parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(root->parse->rtable, + root->parse->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..919e2ec1e9 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..64a00b541b 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RelPermissionInfo, though + * only the tables mentioned in query are assigned RelPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 8ed2c4b8c7..e02174fc79 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -550,7 +551,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -669,6 +670,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +918,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +946,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1105,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1398,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1627,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1874,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2349,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2416,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2435,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2447,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2488,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2776,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3255,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3317,9 +3337,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index c655d188c7..cf1cb81586 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3233,16 +3233,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..8d920eeb7e 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 926dcbf30e..f9bcc8596d 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -82,6 +82,12 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, List **colnames, List **colvars); static int specialAttNum(const char *attname); static bool isQueryUsingTempRelation_walker(Node *node, void *context); +static RelPermissionInfo *AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex); +static RelPermissionInfo *GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok); +static Index GetRelPermissionInfoIndex(List *relpermlist, Oid relid); /* @@ -1021,10 +1027,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1244,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1286,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1430,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1467,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1476,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1497,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1534,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1558,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1567,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1588,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1639,21 +1658,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1946,20 +1959,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1971,7 +1977,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2027,20 +2033,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2115,19 +2114,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,19 +2205,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2239,6 +2226,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2362,19 +2350,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2488,16 +2470,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2509,7 +2488,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3130,6 +3109,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3147,7 +3127,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3696,3 +3679,221 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless one with the same OID is found in it + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + Assert(rte->rtekind == RTE_RELATION); + + Assert(rte->perminfoindex == 0); + return AddRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex); +} + +/* + * AddRelPermissionInfoInternal + * Sub-routine of AddRelPermissionInfo that does the actual work + */ +static RelPermissionInfo * +AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex) +{ + RelPermissionInfo *perminfo; + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfoInternal(*relpermlist, relid, perminfoindex, + true); + if (perminfo) + { + Assert(*perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Note its index. */ + *perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Find RelPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + perminfo = GetRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex, false); + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} + +/* + * GetRelPermissionInfoInternal + * Sub-routine of GetRelPermissionInfo that does the actual work + * + * If *perminfoindex is 0, the list is scanned to find one with given relid. + * If found, *perminfoindex is set to its 1-based index in the list. + * + * If *perminfoindex is already valid (> 0), it means that the caller expects + * to find the entry it's looking for at that location in the list. + */ +static RelPermissionInfo * +GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok) +{ + RelPermissionInfo *perminfo; + + if (*perminfoindex == 0) + { + ListCell *lc; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == relid) + { + *perminfoindex = foreach_current_index(lc) + 1; + return perminfo; + } + } + } + else if (*perminfoindex > 0) + { + + perminfo = (RelPermissionInfo *) + list_nth(relpermlist, *perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * GetRelPermissionInfoIndex + * Returns a 1-based index of the RelPermissionInfo of matching relid if + * found in the given list + * + * 0 indicates that one was not found. + */ +static Index +GetRelPermissionInfoIndex(List *relpermlist, Oid relid) +{ + ListCell *lc; + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(lc); + + if (perminfo->relid == relid) + return foreach_current_index(lc) + 1; + } + + return 0; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source query (src_relpermlist) + * into the destination query's list (*dest_relpermlist), "merging" + * properties of any that are present in both. + * + * Caller must subsequently call ReassignRangeTablePermInfoIndexes() on the + * source query's range table. + */ +void +MergeRelPermissionInfos(List **dest_relpermlist, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *dest_perminfo; + Index ignored = 0; + + dest_perminfo = AddRelPermissionInfoInternal(dest_relpermlist, + src_perminfo->relid, + &ignored); + + dest_perminfo->inh |= src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } +} + +/* + * ReassignRangeTablePermInfoIndexes + * Updates perminfoindex of the relation RTEs in rtable so that they reflect + * their respective RelPermissionInfo entry's current position in permlist. + * + * This is provided for the sites that use MergeRelPermissionInfos() to merge + * the RelPermissionInfo entries of two queries. + */ +void +ReassignRangeTablePermInfoIndexes(List *rtable, List *relpermlist) +{ + ListCell *l; + + foreach(l, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + /* + * Only RELATIONs would have been assigned a RelPermissionInfo and that + * too only those that are mentioned in the query (not inheritance + * child relations that are added afterwards), so we also check that + * the RTE's existing index is valid. + */ + if (rte->rtekind != RTE_RELATION && rte->perminfoindex > 0) + continue; + rte->perminfoindex = GetRelPermissionInfoIndex(relpermlist, rte->relid); + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2a1d44b813..4543949a54 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index b57253463b..e90f2162ca 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1222,7 +1222,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3012,9 +3013,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3070,6 +3068,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3112,8 +3111,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 38e3b1c1b3..bda16358e6 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -491,6 +492,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1787,7 +1790,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1835,7 +1838,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1845,14 +1848,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2cbca4a087..3858186e56 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1110,7 +1110,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 185bf5fbff..9144843fd5 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -787,14 +788,7 @@ 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 rtable entries. - * - * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD - * RTE entry will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * field to the given userid in all RelPermissionInfos of the query. */ void setRuleCheckAsUser(Node *node, Oid userid) @@ -821,18 +815,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RelPermissionInfos for this query. */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 29ae27e5e3..fca2a1eaf0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,27 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(&sub_action->relpermlist, parsetree->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(sub_action->rtable, + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1594,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1623,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3174,6 +3150,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index a233dd4758..d0a292d46c 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index d2aa8d0ca3..d501947912 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 51b3fdc9a0..58d35318c1 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1374,8 +1374,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1393,32 +1393,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1428,9 +1422,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index fa1f589fad..c0914bf4a1 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5168,7 +5168,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5220,7 +5220,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5299,7 +5299,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5347,7 +5347,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5408,6 +5408,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5416,7 +5417,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5485,7 +5486,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index bdb771d278..fd53780908 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -846,8 +846,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RelPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index d68a6b9d28..eb812b3308 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -602,6 +603,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..25ae278deb 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index e2ad761768..a061a49354 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -153,6 +153,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -967,37 +969,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1053,11 +1024,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1177,14 +1154,61 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table, though + * different RTEs for the same relation share the RelPermissionInfo, that + * is, there is only one RelPermissionInfo containing a given relation + * OID (relid). + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 44ffc73f15..9ab9f9df51 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RelPermissionInfos */ + List *finalrelpermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index dca2a21e7a..6417b3cac2 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -71,6 +71,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -702,6 +705,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index cf9c759025..e573b1620f 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -181,6 +181,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +236,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -268,6 +271,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index de21c3c649..923add1176 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,13 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, + RangeTblEntry *rte); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, + RangeTblEntry *rte); +extern void MergeRelPermissionInfos(List **dest_relpermlist, + List *src_relpermlist); +extern void ReassignRangeTablePermInfoIndexes(List *rtable, + List *relpermlist); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.35.3
v15-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 122.2K) download From 714d1bfef685258c3a3168c5bb72361c4a3059e7 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v15 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 34 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 210 ++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/jsonb_sqljson.out | 62 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/sqljson.out | 6 +- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 28 files changed, 758 insertions(+), 837 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 5f2ef88cf3..9902843bf9 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2485,7 +2485,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2548,7 +2548,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6423,10 +6423,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6517,10 +6517,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b97b8b0435..2cbc53b42c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 f0958f03a0..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fca2a1eaf0..6225ccba30 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1859,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 1f08716f69..f626e59c5d 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2216,7 +2216,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2232,7 +2232,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2248,7 +2248,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2264,7 +2264,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2282,7 +2282,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3239,7 +3239,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 601047fa3d..36bed0ac1b 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1544,7 +1544,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1596,7 +1596,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1612,7 +1612,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1628,7 +1628,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2113,15 +2113,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 5ede56d9b5..7315ddb92a 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2475,8 +2475,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2487,8 +2487,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2514,8 +2514,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2527,8 +2527,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 32385bbb0e..e60bdf2dff 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1604,9 +1604,9 @@ alter table tt14t drop column f3; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1627,9 +1627,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1719,8 +1719,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -1965,7 +1965,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2017,19 +2017,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/jsonb_sqljson.out b/src/test/regress/expected/jsonb_sqljson.out index e2f7df50a8..d157e8efdc 100644 --- a/src/test/regress/expected/jsonb_sqljson.out +++ b/src/test/regress/expected/jsonb_sqljson.out @@ -1161,37 +1161,37 @@ SELECT * FROM ); \sv jsonb_table_view CREATE OR REPLACE VIEW public.jsonb_table_view AS - SELECT "json_table".id, - "json_table".id2, - "json_table"."int", - "json_table".text, - "json_table"."char(4)", - "json_table".bool, - "json_table"."numeric", - "json_table".domain, - "json_table".js, - "json_table".jb, - "json_table".jst, - "json_table".jsc, - "json_table".jsv, - "json_table".jsb, - "json_table".jsbq, - "json_table".aaa, - "json_table".aaa1, - "json_table".exists1, - "json_table".exists2, - "json_table".exists3, - "json_table".js2, - "json_table".jsb2w, - "json_table".jsb2q, - "json_table".ia, - "json_table".ta, - "json_table".jba, - "json_table".a1, - "json_table".b1, - "json_table".a11, - "json_table".a21, - "json_table".a22 + SELECT id, + id2, + "int", + text, + "char(4)", + bool, + "numeric", + domain, + js, + jb, + jst, + jsc, + jsv, + jsb, + jsbq, + aaa, + aaa1, + exists1, + exists2, + exists3, + js2, + jsb2w, + jsb2q, + ia, + ta, + jba, + a1, + b1, + a11, + a21, + a22 FROM JSON_TABLE( 'null'::jsonb, '$[*]' AS json_table_path_1 PASSING diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 2334a1321e..2c4da34687 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 7ec3d2688f..fc35d8a567 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1453,10 +1453,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1702,23 +1702,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1732,10 +1732,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1803,13 +1803,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1822,57 +1822,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1895,8 +1895,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1905,13 +1905,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2021,16 +2021,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2068,26 +2068,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2106,41 +2106,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2150,68 +2150,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2228,19 +2228,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2250,19 +2250,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2306,64 +2306,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2548,24 +2548,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3085,7 +3085,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3124,8 +3124,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3137,8 +3137,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3150,8 +3150,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3163,8 +3163,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index aae4ba4939..a9a39900f2 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1059,7 +1059,7 @@ SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING t FROM generate_series(1,5) i; \sv json_objectagg_view CREATE OR REPLACE VIEW public.json_objectagg_view AS - SELECT JSON_OBJECTAGG(i.i : ('111'::text || i.i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.i > 3) AS "json_objectagg" + SELECT JSON_OBJECTAGG(i : ('111'::text || i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i > 3) AS "json_objectagg" FROM generate_series(1, 5) i(i) DROP VIEW json_objectagg_view; -- Test JSON_ARRAYAGG deparsing @@ -1095,7 +1095,7 @@ SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text FROM generate_series(1,5) i; \sv json_arrayagg_view CREATE OR REPLACE VIEW public.json_arrayagg_view AS - SELECT JSON_ARRAYAGG(('111'::text || i.i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.i > 3) AS "json_arrayagg" + SELECT JSON_ARRAYAGG(('111'::text || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3) AS "json_arrayagg" FROM generate_series(1, 5) i(i) DROP VIEW json_arrayagg_view; -- Test JSON_ARRAY(subquery) deparsing @@ -1313,7 +1313,7 @@ SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT \sv is_json_view CREATE OR REPLACE VIEW public.is_json_view AS SELECT '1'::text IS JSON AS "any", - ('1'::text || i.i) IS JSON SCALAR AS scalar, + ('1'::text || i) IS JSON SCALAR AS scalar, NOT '[]'::text IS JSON ARRAY AS "array", '{}'::text IS JSON OBJECT WITH UNIQUE KEYS AS object FROM generate_series(1, 3) i(i) diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index cd812336f2..b09603dc98 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 433a0bb025..7a9968afe2 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 30dd900e11..adbe32c32e 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55ac49be26..73a3a52562 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 493c6186e1..b524e1665f 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-07-27 03:14  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-07-27 03:14 UTC (permalink / raw)
  To: David Rowley <dgrowleyml@gmail.com>; +Cc: Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Jul 13, 2022 at 5:00 PM Amit Langote <amitlangote09@gmail.com> wrote:
> Rebased over 964d01ae90.

Rebased over 2d04277121f.


--
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v16-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 149.7K) download From 4923c4f8738df730c71616c21245557d0bb3ed41 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v16 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table to look for any RTE_RELATION entries to have permissions checked. This arrangement makes permissions-checking needlessly expensive when many inheritance children are added to the range range, because while their permissions need not be checked, the only way to find that out is by seeing requiredPerms == 0 in the their RTEs. This commit moves the permission checking information out of the range table entries into a new plan node called RelPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RelPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the entry's index in the query's list of RelPermissionInfos. --- contrib/postgres_fdw/postgres_fdw.c | 81 +++-- contrib/sepgsql/dml.c | 42 +-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 +- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 105 +++--- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 159 ++++++--- src/backend/nodes/outfuncs.c | 7 +- src/backend/nodes/readfuncs.c | 7 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 123 ++----- src/backend/optimizer/plan/subselect.c | 6 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 170 +++++++--- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 60 +++- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 357 +++++++++++++++----- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 25 +- src/backend/rewrite/rewriteHandler.c | 189 +++++------ src/backend/rewrite/rowsecurity.c | 24 +- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 90 +++-- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 4 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 8 + src/include/rewrite/rewriteHandler.h | 2 +- 50 files changed, 1087 insertions(+), 679 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 048db542d3..793612de30 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,35 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1934,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1942,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1966,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1978,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2145,6 +2180,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2222,6 +2258,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2238,7 +2276,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2624,7 +2663,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2644,13 +2682,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3953,12 +3990,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3970,12 +4007,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..2cf75b6a6d 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 87fdd972c2..603f3928e9 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..7bc85d9eb5 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 3ac731803b..b56b7b4bda 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index a976008b3d..3407486bb3 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1383,7 +1389,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 15a57ea9c3..0d8274d5a7 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1279,7 +1279,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7fbee0c1f7..7a8274ec0a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1204,7 +1204,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9616,7 +9617,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9783,7 +9785,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9989,7 +9992,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10167,7 +10171,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12260,7 +12265,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18006,7 +18012,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18933,7 +18940,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8690a3f3c6..f0958f03a0 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,7 +353,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -397,10 +397,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ef2fd46092..836e5ef344 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,7 +74,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -567,38 +567,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (relpermlist, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -606,32 +607,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f1fd7f7e8b..a89ab74a7e 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index e03ea27299..85a1dc84a6 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -781,7 +783,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -879,7 +882,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1141,7 +1145,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..e9cf39dafb 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored, something that's + * allowed with traditional inheritance. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRelPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RelPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RelPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RelPermissionInfo * +GetResultRelPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RelPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,60 +1318,79 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRelPermissionInfo(estate->es_relpermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = GetResultRelPermissionInfo(relinfo, estate); - return rte->extraUpdatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); } - else - return NULL; + + return perminfo->extraUpdatedCols; } /* Return columns being updated, including generated columns */ diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index a96f2ee8c6..48b72e25b1 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -475,6 +475,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -528,12 +529,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index bee62fc15c..2d22d110d7 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -302,6 +302,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -365,12 +366,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); - READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index e37f2933eb..a4a6b3986b 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5799,7 +5802,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 06ad856eac..bad257655c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5998,6 +6002,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6125,6 +6130,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..c2fcf6f265 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -24,6 +24,7 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -111,9 +112,7 @@ typedef struct #define fix_scan_list(root, lst, rtoffset, num_exec) \ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec)) -static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); -static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); +static void add_rtes_to_flat_rtable(PlannerInfo *root); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, @@ -268,7 +267,7 @@ set_plan_references(PlannerInfo *root, Plan *plan) * will have their rangetable indexes increased by rtoffset. (Additional * RTEs, not referenced by the Plan tree, might get added after those.) */ - add_rtes_to_flat_rtable(root, false); + add_rtes_to_flat_rtable(root); /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list @@ -354,10 +353,17 @@ set_plan_references(PlannerInfo *root, Plan *plan) /* * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * - * This can recurse into subquery plans; "recursing" is true if so. + * Does the same for RelPermissionInfos. This also hunts down subquery RTEs + * of the current plan level whose query was not pulled up into the parent + * query, nor turned into SubqueryScan nodes referenced in the plan tree. Such + * subqueries would not thus have had their RelPermissionInfos merged into + * root->parse->relpermlist, so we must force-add them to + * glob->finalrelpermlist. Failing to do so would prevent the executor from + * performing expected permission checks for tables mentioned in such + * subqueries. */ static void -add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) +add_rtes_to_flat_rtable(PlannerInfo *root) { PlannerGlobal *glob = root->glob; Index rti; @@ -365,34 +371,14 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) /* * Add the query's own RTEs to the flattened rangetable. - * - * 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. - */ - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - } - - /* - * If there are any dead subqueries, they are not referenced in the Plan - * tree, so we must add RTEs contained in them to the flattened rtable - * separately. (If we failed to do this, the executor would not perform - * expected permission checks for tables mentioned in such subqueries.) - * - * Note: this pass over the rangetable can't be combined with the previous - * one, because that would mess up the numbering of the live RTEs in the - * flattened rangetable. */ rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + add_rte_to_flat_rtable(glob, rte); + /* * We should ignore inheritance-parent RTEs: their contents have been * pulled up into our rangetable already. Also ignore any subquery @@ -409,73 +395,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) Assert(rel->relid == rti); /* sanity check on array */ /* - * The subquery might never have been planned at all, if it + * A dead subquery is one that was not planned at all, if it * was excluded on the basis of self-contradictory constraints - * in our query level. In this case apply - * flatten_unplanned_rtes. - * - * If it was planned but the result rel is dummy, we assume - * that it has been omitted from our plan tree (see - * set_subquery_pathlist), and recurse to pull up its RTEs. - * - * Otherwise, it should be represented by a SubqueryScan node - * somewhere in our plan tree, and we'll pull up its RTEs when - * we process that plan node. - * - * However, if we're recursing, then we should pull up RTEs - * whether the subquery is dummy or not, because we've found - * that some upper query level is treating this one as dummy, - * and so we won't scan this level's plan tree at all. + * in our query level, or one that was planned but the result + * rel was dummy. */ - if (rel->subroot == NULL) - flatten_unplanned_rtes(glob, rte); - else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, - UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + if (rte->subquery && rte->subquery->relpermlist && + (rel->subroot == NULL || + IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + UPPERREL_FINAL, NULL)))) + MergeRelPermissionInfos(&glob->finalrelpermlist, + rte->subquery->relpermlist); } + } rti++; } -} -/* - * Extract RangeTblEntries from a subquery that was never planned at all - */ -static void -flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) -{ - /* Use query_tree_walker to find all RTEs in the parse tree */ - (void) query_tree_walker(rte->subquery, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); -} - -static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) -{ - if (node == NULL) - return false; - if (IsA(node, RangeTblEntry)) - { - RangeTblEntry *rte = (RangeTblEntry *) node; + /* Now merge the main query's RelPermissionInfos into the global list. */ + MergeRelPermissionInfos(&glob->finalrelpermlist, root->parse->relpermlist); - /* As above, we need only save relation RTEs */ - if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); - return false; - } - if (IsA(node, Query)) - { - /* Recurse into subselects */ - return query_tree_walker((Query *) node, - flatten_rtes_walker, - (void *) glob, - QTW_EXAMINE_RTES_BEFORE); - } - return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + /* Finally update the flat rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(glob->finalrtable, + glob->finalrelpermlist); } /* @@ -483,10 +425,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to check the relation's permissions. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index df4ca12919..79a7a04032 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1499,6 +1499,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subselect->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * And finally, build the JoinExpr node. */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 0bd99acf83..ae36b47e58 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1211,6 +1204,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + /* Add subquery's RelPermissionInfos into the upper query. */ + MergeRelPermissionInfos(&parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(parse->rtable, parse->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1349,6 +1348,13 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + /* Add the child query's RelPermissionInfos into the parent query. */ + MergeRelPermissionInfos(&root->parse->relpermlist, subquery->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(root->parse->rtable, + root->parse->relpermlist); + /* * Recursively scan the subquery's setOperations tree and add * AppendRelInfo nodes for leaf subqueries to the parent's diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 7e134822f3..919e2ec1e9 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, Assert(partdesc); /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * Note down whether any partition key cols are being updated. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - } - else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,3 +855,82 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, rte); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 520409f4ba..64a00b541b 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RelPermissionInfo, though + * only the tables mentioned in query are assigned RelPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..a0f28222a8 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -550,7 +551,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -669,6 +670,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed such that the target relation's RelPermissionInfo + * is already present in it. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +918,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +946,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1105,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1398,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1627,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1874,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2349,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2416,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2435,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2447,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2488,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2776,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3255,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3344,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 5a18107e79..0f63c1a55a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3227,16 +3227,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..8d920eeb7e 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 8d832efc62..7f78079cd0 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -82,6 +82,12 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, List **colnames, List **colvars); static int specialAttNum(const char *attname); static bool isQueryUsingTempRelation_walker(Node *node, void *context); +static RelPermissionInfo *AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex); +static RelPermissionInfo *GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok); +static Index GetRelPermissionInfoIndex(List *relpermlist, Oid relid); /* @@ -1021,10 +1027,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1244,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1286,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1430,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1467,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1476,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1497,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1534,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1558,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1567,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte); + perminfo->inh |= inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1588,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1662,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1957,20 +1970,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1982,7 +1988,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2038,20 +2044,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2126,19 +2125,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2223,19 +2216,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2250,6 +2237,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2373,19 +2361,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2499,16 +2481,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2520,7 +2499,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3145,6 +3124,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3162,7 +3142,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3714,3 +3697,221 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation and adds it into the + * provided list unless one with the same OID is found in it + * + * Returns the RelPermssionInfo and sets rte->perminfoindex if needed. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, RangeTblEntry *rte) +{ + Assert(rte->rtekind == RTE_RELATION); + + Assert(rte->perminfoindex == 0); + return AddRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex); +} + +/* + * AddRelPermissionInfoInternal + * Sub-routine of AddRelPermissionInfo that does the actual work + */ +static RelPermissionInfo * +AddRelPermissionInfoInternal(List **relpermlist, Oid relid, + Index *perminfoindex) +{ + RelPermissionInfo *perminfo; + + /* + * To prevent duplicate entries for a given relation, check if already in + * the list. + */ + perminfo = GetRelPermissionInfoInternal(*relpermlist, relid, perminfoindex, + true); + if (perminfo) + { + Assert(*perminfoindex >= 0); + return perminfo; + } + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + /* Note its index. */ + *perminfoindex = list_length(*relpermlist); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Find RelPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, RangeTblEntry *rte) +{ + RelPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(relpermlist)) + elog(ERROR, "invalid perminfoindex in RTE with relid %u", + rte->relid); + perminfo = GetRelPermissionInfoInternal(relpermlist, rte->relid, + &rte->perminfoindex, false); + if (rte->relid != perminfo->relid) + elog(ERROR, "permission info at index %u (with OID %u) does not match requested OID %u", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} + +/* + * GetRelPermissionInfoInternal + * Sub-routine of GetRelPermissionInfo that does the actual work + * + * If *perminfoindex is 0, the list is scanned to find one with given relid. + * If found, *perminfoindex is set to its 1-based index in the list. + * + * If *perminfoindex is already valid (> 0), it means that the caller expects + * to find the entry it's looking for at that location in the list. + */ +static RelPermissionInfo * +GetRelPermissionInfoInternal(List *relpermlist, Oid relid, + Index *perminfoindex, + bool missing_ok) +{ + RelPermissionInfo *perminfo; + + if (*perminfoindex == 0) + { + ListCell *lc; + + foreach(lc, relpermlist) + { + perminfo = (RelPermissionInfo *) lfirst(lc); + if (perminfo->relid == relid) + { + *perminfoindex = foreach_current_index(lc) + 1; + return perminfo; + } + } + } + else if (*perminfoindex > 0) + { + + perminfo = (RelPermissionInfo *) + list_nth(relpermlist, *perminfoindex - 1); + Assert(perminfo != NULL && OidIsValid(perminfo->relid)); + + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * GetRelPermissionInfoIndex + * Returns a 1-based index of the RelPermissionInfo of matching relid if + * found in the given list + * + * 0 indicates that one was not found. + */ +static Index +GetRelPermissionInfoIndex(List *relpermlist, Oid relid) +{ + ListCell *lc; + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(lc); + + if (perminfo->relid == relid) + return foreach_current_index(lc) + 1; + } + + return 0; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfos found in a source query (src_relpermlist) + * into the destination query's list (*dest_relpermlist), "merging" + * properties of any that are present in both. + * + * Caller must subsequently call ReassignRangeTablePermInfoIndexes() on the + * source query's range table. + */ +void +MergeRelPermissionInfos(List **dest_relpermlist, List *src_relpermlist) +{ + ListCell *l; + + if (src_relpermlist == NIL) + return; + + foreach(l, src_relpermlist) + { + RelPermissionInfo *src_perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *dest_perminfo; + Index ignored = 0; + + dest_perminfo = AddRelPermissionInfoInternal(dest_relpermlist, + src_perminfo->relid, + &ignored); + + dest_perminfo->inh |= src_perminfo->inh; + dest_perminfo->requiredPerms |= src_perminfo->requiredPerms; + if (!OidIsValid(dest_perminfo->checkAsUser)) + dest_perminfo->checkAsUser = src_perminfo->checkAsUser; + dest_perminfo->selectedCols = bms_union(dest_perminfo->selectedCols, + src_perminfo->selectedCols); + dest_perminfo->insertedCols = bms_union(dest_perminfo->insertedCols, + src_perminfo->insertedCols); + dest_perminfo->updatedCols = bms_union(dest_perminfo->updatedCols, + src_perminfo->updatedCols); + dest_perminfo->extraUpdatedCols = bms_union(dest_perminfo->extraUpdatedCols, + src_perminfo->extraUpdatedCols); + } +} + +/* + * ReassignRangeTablePermInfoIndexes + * Updates perminfoindex of the relation RTEs in rtable so that they reflect + * their respective RelPermissionInfo entry's current position in permlist. + * + * This is provided for the sites that use MergeRelPermissionInfos() to merge + * the RelPermissionInfo entries of two queries. + */ +void +ReassignRangeTablePermInfoIndexes(List *rtable, List *relpermlist) +{ + ListCell *l; + + foreach(l, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + /* + * Only RELATIONs would have been assigned a RelPermissionInfo and that + * too only those that are mentioned in the query (not inheritance + * child relations that are added afterwards), so we also check that + * the RTE's existing index is valid. + */ + if (rte->rtekind != RTE_RELATION && rte->perminfoindex > 0) + continue; + rte->perminfoindex = GetRelPermissionInfoIndex(relpermlist, rte->relid); + } +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 16a0fe59e2..fbe1172708 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index b57253463b..e90f2162ca 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1222,7 +1222,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3012,9 +3013,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3070,6 +3068,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3112,8 +3111,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 5f8c541763..5a670761a4 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -491,6 +492,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1787,7 +1790,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1835,7 +1838,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1845,14 +1848,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index a3c1ba8a40..22c89c8eb2 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1129,7 +1129,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index a5a1fb887f..8baa394f1a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -785,14 +786,7 @@ 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 rtable entries. - * - * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD - * RTE entry will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * field to the given userid in all RelPermissionInfos of the query. */ void setRuleCheckAsUser(Node *node, Oid userid) @@ -819,18 +813,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RelPermissionInfos for this query. */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 29ae27e5e3..fca2a1eaf0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,27 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + MergeRelPermissionInfos(&sub_action->relpermlist, parsetree->relpermlist); + + /* Update the combined rtable to reassign their perminfoindexes. */ + ReassignRangeTablePermInfoIndexes(sub_action->rtable, + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1589,16 +1594,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1616,9 +1623,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3174,6 +3150,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, base_rte); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index a233dd4758..d0a292d46c 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index d2aa8d0ca3..d501947912 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1560,6 +1561,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1607,10 +1609,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 51b3fdc9a0..58d35318c1 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1374,8 +1374,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1393,32 +1393,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1428,9 +1422,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d35e5605de..7548c3530d 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5170,7 +5170,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5222,7 +5222,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5301,7 +5301,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5349,7 +5349,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5410,6 +5410,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5418,7 +5419,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5487,7 +5488,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index bdb771d278..fd53780908 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -846,8 +846,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RelPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 3df1c5a97c..af40f21496 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index d68a6b9d28..eb812b3308 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -602,6 +603,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..25ae278deb 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 98fe1abaa2..970637f115 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -151,6 +151,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -964,37 +966,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1050,11 +1021,17 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RelPermissionInfo belonging to + * this RTE (same relid in both) in the query's list of RelPermissionInfos; + * 0 in non-RELATION RTEs. It's set when the RTE is passed to + * AddRelPermissionInfo() right after its creation in the parser. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1174,14 +1151,61 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table, though + * different RTEs for the same relation share the RelPermissionInfo, that + * is, there is only one RelPermissionInfo containing a given relation + * OID (relid). + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index e2081db4ed..f5208706c3 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RelPermissionInfos */ + List *finalrelpermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index dca2a21e7a..6417b3cac2 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -71,6 +71,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -702,6 +705,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..4e64c0b0e5 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -181,6 +181,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +236,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +274,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RelPermissionInfo *p_perminfo; /* The relation's permissions entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index de21c3c649..923add1176 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,13 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, + RangeTblEntry *rte); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, + RangeTblEntry *rte); +extern void MergeRelPermissionInfos(List **dest_relpermlist, + List *src_relpermlist); +extern void ReassignRangeTablePermInfoIndexes(List *rtable, + List *relpermlist); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..f21786da35 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.35.3
v16-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 123.0K) download From 7ecdad0e84b3a5f159d9e68a32a0fd9068ce41fd Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v16 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RelPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add an RTE for view relations. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteHandler.c | 34 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 222 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/jsonb_sqljson.out | 62 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/sqljson.out | 6 +- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 28 files changed, 764 insertions(+), 843 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index ade797159d..552ec5dda3 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2528,7 +2528,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2591,7 +2591,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6466,10 +6466,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6560,10 +6560,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b97b8b0435..2cbc53b42c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 f0958f03a0..2519970914 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -339,78 +339,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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -573,12 +501,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fca2a1eaf0..6225ccba30 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,27 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before modifying, store a copy of itself so as to serve as the entry + * to be used by the executor to lock the view relation and for the + * planner to be able to record the view relation OID in the PlannedStmt + * that it produces for the query. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1859,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index b10e1c4c0d..5b98ebf089 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2216,7 +2216,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2232,7 +2232,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2248,7 +2248,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2264,7 +2264,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2282,7 +2282,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3261,7 +3261,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 601047fa3d..36bed0ac1b 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1544,7 +1544,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1596,7 +1596,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1612,7 +1612,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1628,7 +1628,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2113,15 +2113,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index d63f4f1cba..f0d5531db0 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2479,8 +2479,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2491,8 +2491,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2518,8 +2518,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2531,8 +2531,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index a828b1f6de..b34429d713 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1623,11 +1623,11 @@ returning pg_describe_object(classid, objid, objsubid) as obj, alter table tt14t drop column f3; -- column f3 is still in the view, sort of ... select pg_get_viewdef('tt14v', true); - pg_get_viewdef ---------------------------------- - SELECT t.f1, + - t."?dropped?column?" AS f3,+ - t.f4 + + pg_get_viewdef +------------------------------- + SELECT f1, + + "?dropped?column?" AS f3,+ + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1675,9 +1675,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1697,8 +1697,8 @@ create view tt14v as select t.f1, t.f4 from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1712,8 +1712,8 @@ alter table tt14t drop column f3; -- ok select pg_get_viewdef('tt14v', true); pg_get_viewdef ---------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1806,8 +1806,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -2052,7 +2052,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2104,19 +2104,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/jsonb_sqljson.out b/src/test/regress/expected/jsonb_sqljson.out index ef496110af..be770482c5 100644 --- a/src/test/regress/expected/jsonb_sqljson.out +++ b/src/test/regress/expected/jsonb_sqljson.out @@ -1161,37 +1161,37 @@ SELECT * FROM ); \sv jsonb_table_view CREATE OR REPLACE VIEW public.jsonb_table_view AS - SELECT "json_table".id, - "json_table".id2, - "json_table"."int", - "json_table".text, - "json_table"."char(4)", - "json_table".bool, - "json_table"."numeric", - "json_table".domain, - "json_table".js, - "json_table".jb, - "json_table".jst, - "json_table".jsc, - "json_table".jsv, - "json_table".jsb, - "json_table".jsbq, - "json_table".aaa, - "json_table".aaa1, - "json_table".exists1, - "json_table".exists2, - "json_table".exists3, - "json_table".js2, - "json_table".jsb2w, - "json_table".jsb2q, - "json_table".ia, - "json_table".ta, - "json_table".jba, - "json_table".a1, - "json_table".b1, - "json_table".a11, - "json_table".a21, - "json_table".a22 + SELECT id, + id2, + "int", + text, + "char(4)", + bool, + "numeric", + domain, + js, + jb, + jst, + jsc, + jsv, + jsb, + jsbq, + aaa, + aaa1, + exists1, + exists2, + exists3, + js2, + jsb2w, + jsb2q, + ia, + ta, + jba, + a1, + b1, + a11, + a21, + a22 FROM JSON_TABLE( 'null'::jsonb, '$[*]' AS json_table_path_1 PASSING diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e2e62db6a2..fbb840e848 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 7ec3d2688f..fc35d8a567 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1453,10 +1453,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1702,23 +1702,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1732,10 +1732,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1803,13 +1803,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1822,57 +1822,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1895,8 +1895,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1905,13 +1905,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2021,16 +2021,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2068,26 +2068,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2106,41 +2106,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2150,68 +2150,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2228,19 +2228,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2250,19 +2250,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2306,64 +2306,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2548,24 +2548,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3085,7 +3085,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3124,8 +3124,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3137,8 +3137,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3150,8 +3150,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3163,8 +3163,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index bdd0969a50..6b9cddc829 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -1059,7 +1059,7 @@ SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING t FROM generate_series(1,5) i; \sv json_objectagg_view CREATE OR REPLACE VIEW public.json_objectagg_view AS - SELECT JSON_OBJECTAGG(i.i : ('111'::text || i.i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.i > 3) AS "json_objectagg" + SELECT JSON_OBJECTAGG(i : ('111'::text || i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i > 3) AS "json_objectagg" FROM generate_series(1, 5) i(i) DROP VIEW json_objectagg_view; -- Test JSON_ARRAYAGG deparsing @@ -1095,7 +1095,7 @@ SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text FROM generate_series(1,5) i; \sv json_arrayagg_view CREATE OR REPLACE VIEW public.json_arrayagg_view AS - SELECT JSON_ARRAYAGG(('111'::text || i.i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.i > 3) AS "json_arrayagg" + SELECT JSON_ARRAYAGG(('111'::text || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3) AS "json_arrayagg" FROM generate_series(1, 5) i(i) DROP VIEW json_arrayagg_view; -- Test JSON_ARRAY(subquery) deparsing @@ -1313,7 +1313,7 @@ SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT \sv is_json_view CREATE OR REPLACE VIEW public.is_json_view AS SELECT '1'::text IS JSON AS "any", - ('1'::text || i.i) IS JSON SCALAR AS scalar, + ('1'::text || i) IS JSON SCALAR AS scalar, NOT '[]'::text IS JSON ARRAY AS "array", '{}'::text IS JSON OBJECT WITH UNIQUE KEYS AS object FROM generate_series(1, 3) i(i) diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 89a34ffbb2..f2b2156d2b 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 433a0bb025..7a9968afe2 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 30dd900e11..adbe32c32e 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 55ac49be26..73a3a52562 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 493c6186e1..b524e1665f 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-07-27 21:04  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 2 replies; 97+ messages in thread

From: Tom Lane @ 2022-07-27 21:04 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Amit Langote <amitlangote09@gmail.com> writes:
> [ v16 patches ]

I took a quick look at this ...

I think that the notion behind MergeRelPermissionInfos, ie that
a single RelPermissionInfo can represent *all* the checks for
a given table OID, is fundamentally wrong.  For example, when
merging a view into an outer query that references a table
also used by the view, the checkAsUser fields might be different,
and the permissions to check might be different, and the columns
those permissions need to hold for might be different.  Blindly
bms_union'ing the column sets will lead to requiring far more
permissions than the query should require.  Conversely, this
approach could lead to allowing cases we should reject, if you
happen to "merge" checkAsUser in a way that ends in checking as a
higher-privilege user than should be checked.

I'm inclined to think that you should abandon the idea of
merging RelPermissionInfos at all.  It can only buy us much
in the case of self-joins, which ought to be rare.  It'd
be better to just say "there is one RelPermissionInfo for
each RTE requiring any sort of permissions check".  Either
that or you need to complicate RelPermissionInfo a lot, but
I don't see the advantage.

It'd likely be better to rename ExecutorCheckPerms_hook,
say to ExecCheckPermissions_hook given the rename of
ExecCheckRTPerms.  As it stands, it *looks* like the API
of that hook has not changed, when it has.  Better to
break calling code visibly than to make people debug their
way to an understanding that the List contents are no longer
what they expected.  A different idea could be to pass both
the rangetable and relpermlist, again making the API break obvious
(and who's to say a hook might not still want the rangetable?)

In parsenodes.h:
+    List       *relpermlist;    /* list of RTEPermissionInfo nodes for
+                                 * the RTE_RELATION entries in rtable */

I find this comment not very future-proof, if indeed it's strictly
correct even today.  Maybe better "list of RelPermissionInfo nodes for
rangetable entries having perminfoindex > 0".  Likewise for the comment
in RangeTableEntry: there's no compelling reason to assume that all and
only RELATION RTEs will have RelPermissionInfo.  Even if that remains
true at parse time it's falsified during planning.

Also note typo in node name: that comment is the only reference to
"RTEPermissionInfo" AFAICS.  Although, given the redefinition I
suggest above, arguably "RTEPermissionInfo" is the better name?

I'm confused as to why RelPermissionInfo.inh exists.  It doesn't
seem to me that permissions checking should care about child rels.

Why did you add checkAsUser to ForeignScan (and not any other scan
plan nodes)?  At best that's pretty asymmetric, but it seems mighty
bogus: under what circumstances would an FDW need to know that but
not any of the other RelPermissionInfo fields?  This seems to
indicate that someplace we should work harder at making the
RelPermissionInfo list available to FDWs.  (CustomScan providers
might have similar issues, btw.)

I've not looked at much of the actual code, just the .h file changes.
Haven't studied 0002 either.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-07-27 21:18  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  1 sibling, 1 reply; 97+ messages in thread

From: Tom Lane @ 2022-07-27 21:18 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

... One more thing: maybe we should rethink where to put
extraUpdatedCols.  Between the facts that it's not used for
actual permissions checks, and that it's calculated by the
rewriter not parser, it doesn't seem like it really belongs
in RelPermissionInfo.  Should we keep it in RangeTblEntry?
Should it go somewhere else entirely?  I'm just speculating,
but now is a good time to think about it.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-09-07 09:23  Amit Langote <amitlangote09@gmail.com>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  1 sibling, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-09-07 09:23 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Jul 28, 2022 at 6:04 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Amit Langote <amitlangote09@gmail.com> writes:
> > [ v16 patches ]
>
> I took a quick look at this ...

Thanks for the review and sorry about the delay.

> I think that the notion behind MergeRelPermissionInfos, ie that
> a single RelPermissionInfo can represent *all* the checks for
> a given table OID, is fundamentally wrong.  For example, when
> merging a view into an outer query that references a table
> also used by the view, the checkAsUser fields might be different,
> and the permissions to check might be different, and the columns
> those permissions need to hold for might be different.  Blindly
> bms_union'ing the column sets will lead to requiring far more
> permissions than the query should require.  Conversely, this
> approach could lead to allowing cases we should reject, if you
> happen to "merge" checkAsUser in a way that ends in checking as a
> higher-privilege user than should be checked.
>
> I'm inclined to think that you should abandon the idea of
> merging RelPermissionInfos at all.  It can only buy us much
> in the case of self-joins, which ought to be rare.  It'd
> be better to just say "there is one RelPermissionInfo for
> each RTE requiring any sort of permissions check".  Either
> that or you need to complicate RelPermissionInfo a lot, but
> I don't see the advantage.

OK, I agree that the complexity of sharing a RelPermissionInfo between
RTEs far exceeds any performance benefit to be had from it.

I have changed things so that there's one RelPermissionInfo for every
RTE_RELATION entry in the range table, except those that the planner
adds when expanding inheritance.

> It'd likely be better to rename ExecutorCheckPerms_hook,
> say to ExecCheckPermissions_hook given the rename of
> ExecCheckRTPerms.  As it stands, it *looks* like the API
> of that hook has not changed, when it has.  Better to
> break calling code visibly than to make people debug their
> way to an understanding that the List contents are no longer
> what they expected.  A different idea could be to pass both
> the rangetable and relpermlist, again making the API break obvious
> (and who's to say a hook might not still want the rangetable?)

I agree it'd be better to break the API more explicitly.  Actually, I
decided to adopt both of these suggestions: renamed the hook and kept
the rangeTable parameter.

> In parsenodes.h:
> +    List       *relpermlist;    /* list of RTEPermissionInfo nodes for
> +                                 * the RTE_RELATION entries in rtable */
>
> I find this comment not very future-proof, if indeed it's strictly
> correct even today.  Maybe better "list of RelPermissionInfo nodes for
> rangetable entries having perminfoindex > 0".  Likewise for the comment
> in RangeTableEntry: there's no compelling reason to assume that all and
> only RELATION RTEs will have RelPermissionInfo.  Even if that remains
> true at parse time it's falsified during planning.

Ah right, inheritance children's RTE_RELATION entries don't have one.
I've fixed the comment.

> Also note typo in node name: that comment is the only reference to
> "RTEPermissionInfo" AFAICS.  Although, given the redefinition I
> suggest above, arguably "RTEPermissionInfo" is the better name?

Agreed.  I've renamed RelPermissionInfo to RTEPermissionInfo and
relpermlist to rtepermlist.

> I'm confused as to why RelPermissionInfo.inh exists.  It doesn't
> seem to me that permissions checking should care about child rels.

I had to do this for contrib/sepgsql, sepgsql_dml_privileges() has this:

        /*
         * If this RangeTblEntry is also supposed to reference inherited
         * tables, we need to check security label of the child tables. So, we
         * expand rte->relid into list of OIDs of inheritance hierarchy, then
         * checker routine will be invoked for each relations.
         */
        if (!rte->inh)
            tableIds = list_make1_oid(rte->relid);
        else
            tableIds = find_all_inheritors(rte->relid, NoLock, NULL);

> Why did you add checkAsUser to ForeignScan (and not any other scan
> plan nodes)?  At best that's pretty asymmetric, but it seems mighty
> bogus: under what circumstances would an FDW need to know that but
> not any of the other RelPermissionInfo fields?  This seems to
> indicate that someplace we should work harder at making the
> RelPermissionInfo list available to FDWs.  (CustomScan providers
> might have similar issues, btw.)

I think I had tried doing what you are suggesting -- getting the
checkAsUser from a RelPermissionInfo rather than putting that in
ForeignScan -- though we can't do it, because we need the userid for
child foreign table relations, for which we don't create a
RelPermissionInfo.  ForeignScan nodes for child relations don't store
their root parent's RT index, so we can't get the checkAsUser using
the root parent's RelPermissionInfo, like I could do for child foreign
table "result" relations using ResultRelInfo.ri_RootResultRelInfo.

As to why an FDW may not need to know any of the other
RelPermissionInfo fields, IIUC, ExecCheckPermissions() would have done
everything that ought to be done *locally* using that information.
Whatever the remote side needs to know wrt access permission checking
should have been put in fdw_private, no?

On Thu, Jul 28, 2022 at 6:18 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> ... One more thing: maybe we should rethink where to put
> extraUpdatedCols.  Between the facts that it's not used for
> actual permissions checks, and that it's calculated by the
> rewriter not parser, it doesn't seem like it really belongs
> in RelPermissionInfo.  Should we keep it in RangeTblEntry?
> Should it go somewhere else entirely?  I'm just speculating,
> but now is a good time to think about it.

Indeed, extraUpdatedCols doesn't really seem to belong in
RelPermissionInfo, so I have left it in RangeTblEntry.

Attached updated patches.
--
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v17-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 145.6K) download From 38dea7ad84fd983e17605f131c128294e8049774 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v17 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table looking for relations that need to have permissions checked. This can make the permission checking part of the executor initialization needlessly expensive when many inheritance children are present in the range range. While the permissions need not be checked on the individual child relations, the executor still must visit every range table entry to filter them out. This commit moves the permission checking information out of the range table entries into a new plan node called RTEPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RTEPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the correponding RTEPermissionInfo's index in the query's list of the latter. --- contrib/postgres_fdw/postgres_fdw.c | 82 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 12 +- contrib/sepgsql/sepgsql.h | 3 +- src/backend/access/common/attmap.c | 14 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 17 +- src/backend/commands/copyfrom.c | 11 +- src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 115 +++++------ src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 133 +++++++++---- src/backend/nodes/outfuncs.c | 7 +- src/backend/nodes/readfuncs.c | 7 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 43 +++- src/backend/optimizer/plan/subselect.c | 7 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 155 +++++++++++---- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 65 +++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 175 +++++++++-------- src/backend/parser/parse_target.c | 18 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 11 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 27 +-- src/backend/rewrite/rewriteHandler.c | 183 ++++++++---------- src/backend/rewrite/rewriteManip.c | 25 +++ src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 19 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 11 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 95 +++++---- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 9 +- src/include/parser/parse_relation.h | 4 + src/include/rewrite/rewriteHandler.h | 1 + src/include/rewrite/rewriteManip.h | 2 + .../modules/test_oat_hooks/test_oat_hooks.c | 12 +- 54 files changed, 951 insertions(+), 565 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 16320170ce..7a8521d03f 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,36 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1935,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1943,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1967,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1979,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2147,6 +2183,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2224,6 +2261,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2240,7 +2279,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2626,7 +2666,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2646,13 +2685,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3955,12 +3993,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3972,12 +4010,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..c4e071b0ea 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -23,6 +23,7 @@ #include "commands/tablecmds.h" #include "executor/executor.h" #include "nodes/bitmapset.h" +#include "parser/parsetree.h" #include "sepgsql.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -277,38 +278,33 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, + bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, rtepermlist) { - RangeTblEntry *rte = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +335,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 87fdd972c2..129442b96e 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -35,7 +35,7 @@ PG_MODULE_MAGIC; * Saved hook entries (if stacked) */ static object_access_hook_type next_object_access_hook = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *rangeTbls, List *rtepermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (rangeTbls, rtepermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(rangeTbls, rtepermlist, abort)) return false; return true; @@ -471,8 +471,8 @@ _PG_init(void) object_access_hook = sepgsql_object_access; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = sepgsql_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = sepgsql_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f2a2c795bf..7aa6df92ec 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object, /* * dml.c */ -extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation); +extern bool sepgsql_dml_privileges(List *rangeTabls, List *rtepermlist, + bool abort_on_violation); /* * database.c diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..1e65d8a120 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,15 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error; AttrMap.attnums[] entry for such an + * outdesc column will be 0 in that case. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +240,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +262,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 49924e476a..5a62d5641d 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +155,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +177,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index e8bb168aea..6090769a68 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the RTEPermissionInfos into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_rtepermlist = cstate->rtepermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1380,9 +1386,12 @@ BeginCopyFrom(ParseState *pstate, initStringInfo(&cstate->attribute_buf); - /* Assign range table, we'll need it in CopyFrom. */ + /* Assign range table and rtepermlist, we'll need them in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->rtepermlist = pstate->p_rtepermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 3c6e09815e..16ef4cab3a 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1287,7 +1287,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dacc989d85..b9fd58d349 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1206,7 +1206,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9647,7 +9648,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9814,7 +9816,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10020,7 +10023,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10198,7 +10202,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12291,7 +12296,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18039,7 +18045,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18966,7 +18973,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index b5a0fc02e5..6f07ac2a9c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -411,10 +411,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ef2fd46092..5eed90c722 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,8 +74,8 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ -ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; +/* Hook for plugin to get control in ExecCheckPermissions() */ +ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook = NULL; /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +565,63 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rtepermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } - if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, - ereport_on_violation); + if (ExecutorCheckPermissions_hook) + result = (*ExecutorCheckPermissions_hook) (rangeTable, rtepermlist, + ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->rtepermlist, true); + estate->es_rtepermlist = plannedstmt->rtepermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f1fd7f7e8b..05ff93e3c6 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->rtepermlist = estate->es_rtepermlist; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 901dd435ef..b992dbc657 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -780,7 +782,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -878,7 +881,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1140,7 +1144,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..461230b011 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent, something that's allowed with + * traditional inheritance, are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRTEPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RTEPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RTEPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,41 +1318,64 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 60610e3a4b..c8f52ae11d 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -475,6 +475,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -528,12 +529,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index bee62fc15c..2d22d110d7 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -302,6 +302,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_INT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -365,12 +366,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); - READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index cd8a3ef7cb..5b1416c9fa 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5794,7 +5797,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 079bd0bfdf..c75fb92eb8 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrtepermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrtepermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -520,6 +523,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->rtepermlist = glob->finalrtepermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6208,6 +6212,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6335,6 +6340,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..a26aa36048 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -355,6 +355,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rtepermlist. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -370,14 +373,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ + rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) + { + /* + * Update perminfoindex, if any, to reflect the correponding + * RTEPermissionInfo's position in the flattened list. + */ + if (rte->perminfoindex > 0) + rte->perminfoindex += list_length(glob->finalrtepermlist); + add_rte_to_flat_rtable(glob, rte); + } + + rti++; } + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + root->parse->rtepermlist); + /* * If there are any dead subqueries, they are not referenced in the Plan * tree, so we must add RTEs contained in them to the flattened rtable @@ -450,6 +468,15 @@ flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) flatten_rtes_walker, (void *) glob, QTW_EXAMINE_RTES_BEFORE); + + /* + * Now add the subquery's RTEPermissionInfos too. flatten_rtes_walker() + * should already have updated the perminfoindex in the RTEs in the + * subquery to reflect the corresponding RTEPermissionInfos' position in + * finalrtepermlist. + */ + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + rte->subquery->rtepermlist); } static bool @@ -463,7 +490,15 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) + { + /* + * The correponding RTEPermissionInfo will get added to + * finalrtepermlist, so adjust perminfoindex accordingly. + */ + Assert(rte->perminfoindex > 0); + rte->perminfoindex += list_length(glob->finalrtepermlist); add_rte_to_flat_rtable(glob, rte); + } return false; } if (IsA(node, Query)) @@ -483,10 +518,10 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo for executor-startup + * permission checking. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..a61082d27c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,6 +1496,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, + subselect->rtable); + /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 41c7066d90..607f7ae907 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1205,6 +1198,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, + subquery->rtable); + /* * Now append the adjusted rtable entries to upper query. (We hold off * until after fixing the upper rtable entries; no point in running that @@ -1345,6 +1345,12 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, + subquery->rtepermlist, rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index cf7691a474..10c2aa13f6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *root_perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +312,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +332,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains the root partrel's + * updatedCols translated to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +409,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +468,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +491,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,33 +553,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ + /* Translate the bitmapset of generated columns being updated. */ if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); - } else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which @@ -866,3 +859,83 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * We need to get the updatedCols bitmapset from the relation's + * RTEPermissionInfo. + * + * If it's a simple "base" rel, can just fetch its RTEPermissionInfo that + * must always be present; this cannot get called on non-RELATION RTEs. + * + * For "other" rels, must look up the root parent relation mentioned in the + * query, because only that one gets assigned a RTEPermissionInfo, and + * translate the columns found therein to match the given relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + Assert(rte->perminfoindex > 0); + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + if (rel->top_parent_relids != NULL) + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + else + updatedCols = perminfo->updatedCols; + + /* extraUpdatedCols can be obtained directly from the RTE. */ + rte = planner_rt_fetch(rel->relid, root); + extraUpdatedCols = rte->extraUpdatedCols; + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index edcdd0a360..7711075ac2 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though + * only the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..25324d9486 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; + List *sub_rtepermlist; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -594,10 +596,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing - * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in - * that case we want the rule's OLD and NEW rtable entries to appear as - * part of the SELECT's rtable, not as outer references for it. (Kluge!) + * INSERT/SELECT, arrange to pass the rangetable/rtepermlist/namespace down + * to the SELECT. This can only happen if we are inside a CREATE RULE, + * and in that case we want the rule's OLD and NEW rtable entries to appear + * as part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ @@ -605,6 +607,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; + sub_rtepermlist = pstate->p_rtepermlist; + pstate->p_rtepermlist = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } @@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_rtepermlist = sub_rtepermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +899,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +915,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1102,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2432,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2444,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2485,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3252,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3341,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 061d0bcc50..6e301fccbe 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3224,16 +3224,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RTEPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..5781a4b995 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index f44937a8bb..f8a6ccef8a 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,10 +1021,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1238,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RTEPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1280,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1424,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1461,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1470,8 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1485,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1522,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1546,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1555,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1570,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1644,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1974,20 +1969,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1999,7 +1987,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2066,20 +2054,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2154,19 +2135,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2251,19 +2226,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2278,6 +2247,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2401,19 +2371,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2527,16 +2491,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2548,7 +2509,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3173,6 +3134,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3190,7 +3152,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3742,3 +3707,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRTEPermissionInfo + * Creates RTEPermissionInfo for a given RTE and adds it into the + * provided list + * + * Returns the RTEPermissionInfo and sets rte->perminfoindex. + */ +RTEPermissionInfo * +AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex == 0); + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RTEPermissionInfo); + perminfo->relid = rte->relid; + perminfo->inh = rte->inh; + /* Other information is set by fetching the node as and where needed. */ + + *rtepermlist = lappend(*rtepermlist, perminfo); + + /* Note its index. */ + rte->perminfoindex = list_length(*rtepermlist); + + return perminfo; +} + +/* + * GetRTEPermissionInfo + * Find RTEPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RTEPermissionInfo * +GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + rte->perminfoindex, rte->relid); + perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL); + if (perminfo->relid != rte->relid) + elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 4e1593d900..e0a06d55f8 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1141,7 +1141,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1423,11 +1427,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * with its RTEPermissionInfo's selectedCols bitmap showing the whole row + * as needing select permission, as well as the individual columns. + * However, we can only get here for weird notations like (table.*).*, so + * it's not worth trying to clean up --- arguably, the permissions marking + * is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 6d283006e3..66bba6e72f 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1232,7 +1232,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3022,9 +3023,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3080,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->rtepermlist = pstate->p_rtepermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3122,8 +3121,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 5f8c541763..491f5aa9c6 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -491,6 +492,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRTEPermissionInfo(&estate->es_rtepermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1788,6 +1791,7 @@ apply_handle_update(StringInfo s) bool has_oldtup; TupleTableSlot *remoteslot; RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1836,6 +1840,7 @@ apply_handle_update(StringInfo s) * on the subscriber, since we are not touching those. */ target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1845,14 +1850,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 62e0ffecd8..ad3f4cb15e 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1129,7 +1129,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 213eabfbb9..5e9d226e54 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -785,14 +785,14 @@ 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 rtable entries. + * 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 will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * 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) @@ -819,18 +819,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RTEPermissionInfos for this query. */ + foreach(l, qry->rtepermlist) + { + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 29ae27e5e3..f9b8dc3e6f 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -352,6 +352,7 @@ rewriteRuleAction(Query *parsetree, Query *sub_action; Query **sub_action_ptr; acquireLocksOnSubLinks_context context; + List *query_rtable; context.for_execute = true; @@ -394,32 +395,35 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. + * Also merge RTEPermissionInfo lists to ensure that all permissions are + * checked correctly. * * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. - * - * NOTE: because planner will destructively alter rtable, we must ensure - * that rule action's rtable is separate and shares no substructure with - * the main rtable. Hence do a deep copy here. + * NOTE: because planner will destructively alter rtable and rtepermlist, + * we must ensure that rule action's lists are separate and shares no + * substructure with the main query's lists. Hence do a deep copy here + * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), + * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtable = list_concat(copyObject(parsetree->rtable), - sub_action->rtable); + sub_action->rtepermlist = copyObject(sub_action->rtepermlist); + query_rtable = copyObject(parsetree->rtable); + ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, + parsetree->rtepermlist, query_rtable); + sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1590,10 +1594,13 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; @@ -1616,7 +1623,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RTEPermissionInfo *view_perminfo; + RTEPermissionInfo *base_perminfo; + RTEPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3175,6 +3151,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); Assert(base_rte->rtekind == RTE_RELATION); + base_perminfo = GetRTEPermissionInfo(viewquery->rtepermlist, base_rte); /* * Up to now, the base relation hasn't been touched at all in our query. @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RTEPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRTEPermissionInfo(&parsetree->rtepermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 101c39553a..3552a8db59 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1531,3 +1531,28 @@ ReplaceVarsFromTargetList(Node *node, (void *) &context, outer_hasSubLinks); } + +/* + * ConcatRTEPermissionInfoLists + * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * + * Also updates perminfoindex of the RTEs in src_rtable to point to the + * "source" perminfos after they have been added into *dest_rtepermlist. + */ +void +ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable) +{ + ListCell *l; + int offset = list_length(*dest_rtepermlist); + + *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + + foreach(l, src_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (rte->perminfoindex > 0) + rte->perminfoindex += offset; + } +} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b2a7237430..e4ce49d606 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RTEPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRTEPermissionInfo(root->rtepermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ee05e230e0..c8a5d5c4b9 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1598,6 +1599,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo; int clause_relid; Oid userid; @@ -1646,10 +1648,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); /* Table-level SELECT privilege is sufficient for all columns */ if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 51b3fdc9a0..86b7aeb6f5 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1376,6 +1376,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; + RTEPermissionInfo *pk_perminfo; + RTEPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1398,27 +1400,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; + + pk_perminfo = makeNode(RTEPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RTEPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fkrte, pkrte), + list_make2(fk_perminfo, pk_perminfo), false)) return false; /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 50b588e3d0..2e3d8df526 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5170,7 +5170,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5222,7 +5222,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5301,7 +5301,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5349,7 +5349,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5410,6 +5410,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5418,7 +5419,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5487,7 +5488,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 00dc0f2403..7c439a3cfb 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RTEPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index e37c6032ae..4fbb8801ff 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rtepermlist; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 82925b4b63..72634171f0 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,9 +80,9 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); -extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPermissions_hook_type) (List *, List *, bool); +extern PGDLLIMPORT ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook; /* @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rtepermlist, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..c32834a9e8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rtepermlist; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6958306a7d..14231ce0a8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -151,6 +151,9 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -964,37 +967,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1050,11 +1022,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1174,14 +1151,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 294cfe9c47..478353e26f 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrtepermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index dca2a21e7a..a981c0bbf4 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -71,6 +71,10 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -702,6 +706,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* user to perform the scan as */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..69665aba41 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rtepermlist: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rtepermlist; /* list of RTEPermissionInfo nodes for + * each RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rtepermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rtepermlist entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index de21c3c649..11bd11f40d 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,9 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RTEPermissionInfo *AddRTEPermissionInfo(List **rtepermlist, + RangeTblEntry *rte); +extern RTEPermissionInfo *GetRTEPermissionInfo(List *rtepermlist, + RangeTblEntry *rte); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..05c3680cd6 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index 98b9b3a288..607405009c 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,5 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); +extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable); #endif /* REWRITEMANIP_H */ diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c index 4b4e259cd2..bfa9263233 100644 --- a/src/test/modules/test_oat_hooks/test_oat_hooks.c +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -46,7 +46,7 @@ static bool REGRESS_suset_variable2 = false; /* Saved hook values */ static object_access_hook_type next_object_access_hook = NULL; static object_access_hook_type_str next_object_access_hook_str = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* Test Object Access Type Hook hooks */ @@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access, int subId, void *arg); static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg); -static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static bool REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort); static void REGRESS_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, @@ -219,8 +219,8 @@ _PG_init(void) object_access_hook_str = REGRESS_object_access_hook_str; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = REGRESS_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; @@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i } static bool -REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort) { bool am_super = superuser_arg(GetUserId()); bool allow = true; @@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) /* Forward to next hook in the chain */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + !(*next_exec_check_perms_hook) (rangeTabls, rtepermlist, do_abort)) allow = false; if (allow) -- 2.35.3
v17-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 120.6K) download From 444b8ea4b1015a3f02fdfb3d4f6ead2a157f2c3b Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v17 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RTEPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add a copy of the original view RTE. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteDefine.c | 7 - src/backend/rewrite/rewriteHandler.c | 33 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 222 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 27 files changed, 729 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 7bf35602b0..76b4668779 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2528,7 +2528,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2591,7 +2591,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6474,10 +6474,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6591,10 +6591,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b97b8b0435..2cbc53b42c 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 6f07ac2a9c..7e3d5e79bc 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,78 +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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -587,12 +515,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 5e9d226e54..ac2568e59a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -786,13 +786,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 f9b8dc3e6f..0fe1090cd8 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before converting the RTE to become a SUBQUERY, store a copy for the + * executor to be able to lock the view relation and for the planner to be + * able to record the view relation OID in PlannedStmt.relationOids. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1858,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 2873b662fb..a068a5a873 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2225,7 +2225,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2241,7 +2241,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2257,7 +2257,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2273,7 +2273,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2291,7 +2291,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3270,7 +3270,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index b2198724e3..4a55e42dd2 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1622,7 +1622,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1674,7 +1674,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1690,7 +1690,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1706,7 +1706,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2191,15 +2191,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index d63f4f1cba..f0d5531db0 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2479,8 +2479,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2491,8 +2491,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2518,8 +2518,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2531,8 +2531,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index a828b1f6de..b34429d713 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1623,11 +1623,11 @@ returning pg_describe_object(classid, objid, objsubid) as obj, alter table tt14t drop column f3; -- column f3 is still in the view, sort of ... select pg_get_viewdef('tt14v', true); - pg_get_viewdef ---------------------------------- - SELECT t.f1, + - t."?dropped?column?" AS f3,+ - t.f4 + + pg_get_viewdef +------------------------------- + SELECT f1, + + "?dropped?column?" AS f3,+ + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1675,9 +1675,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1697,8 +1697,8 @@ create view tt14v as select t.f1, t.f4 from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1712,8 +1712,8 @@ alter table tt14t drop column f3; -- ok select pg_get_viewdef('tt14v', true); pg_get_viewdef ---------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1806,8 +1806,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -2052,7 +2052,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2104,19 +2104,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e2e62db6a2..fbb840e848 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 9dd137415e..19ef0a6b80 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1448,10 +1448,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1697,23 +1697,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1727,10 +1727,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1798,13 +1798,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1817,57 +1817,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1890,8 +1890,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1900,13 +1900,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2016,16 +2016,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2101,41 +2101,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2145,68 +2145,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2223,19 +2223,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2245,19 +2245,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2301,64 +2301,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2543,24 +2543,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3080,7 +3080,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3119,8 +3119,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3132,8 +3132,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3145,8 +3145,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3158,8 +3158,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 8fe42ec560..b8cbc0cf0f 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 55dcd668c9..ddaefdd91d 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 30dd900e11..adbe32c32e 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 948b4e702c..cc213523c0 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 5fd3886b5e..3986fc1706 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-02 17:10  Andres Freund <andres@anarazel.de>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Andres Freund @ 2022-10-02 17:10 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; Justin Pryzby <pryzbyj@telsasoft.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,

On 2022-09-07 18:23:06 +0900, Amit Langote wrote:
> Attached updated patches.

Thanks to Justin's recent patch (89d16b63527) to add
-DRELCACHE_FORCE_RELEASE -DCOPY_PARSE_PLAN_TREES -DWRITE_READ_PARSE_PLAN_TREES -DRAW_EXPRESSION_COVERAGE_TEST
to the FreeBSD ci task we now see the following:

https://cirrus-ci.com/task/4772259058417664
https://api.cirrus-ci.com/v1/artifact/task/4772259058417664/testrun/build/testrun/main/regress/regre...

inline patch 1: /tmp/cirrus-ci-build/build/testrun/main/regress/results/updatable_views.out (+4/-2 lines) download inline-1.patchdiff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/updatable_views.out /tmp/cirrus-ci-build/build/testrun/main/regress/results/updatable_views.out --- /tmp/cirrus-ci-build/src/test/regress/expected/updatable_views.out 2022-10-02 10:37:08.888945000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/main/regress/results/updatable_views.out 2022-10-02 10:40:26.947887000 +0000 @@ -1727,14 +1727,16 @@ (4 rows) UPDATE base_tbl SET id = 2000 WHERE id = 2; +WARNING: outfuncs/readfuncs failed to produce an equal rewritten parse tree UPDATE rw_view1 SET id = 3000 WHERE id = 3; +WARNING: outfuncs/readfuncs failed to produce an equal rewritten parse tree SELECT * FROM base_tbl; id | idplus1 ------+--------- 1 | 2 4 | 5 - 2000 | 2001 - 3000 | 3001 + 2000 | 3 + 3000 | 4 (4 rows) DROP TABLE base_tbl CASCADE;
and many more. Greetings, Andres Freund ^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-03 09:10  Amit Langote <amitlangote09@gmail.com>
  parent: Andres Freund <andres@anarazel.de>
  0 siblings, 0 replies; 97+ messages in thread

From: Amit Langote @ 2022-10-03 09:10 UTC (permalink / raw)
  To: Andres Freund <andres@anarazel.de>; +Cc: Justin Pryzby <pryzbyj@telsasoft.com>; Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hi,

On Mon, Oct 3, 2022 at 2:10 AM Andres Freund <andres@anarazel.de> wrote:
> On 2022-09-07 18:23:06 +0900, Amit Langote wrote:
> > Attached updated patches.
>
> Thanks to Justin's recent patch (89d16b63527) to add
> -DRELCACHE_FORCE_RELEASE -DCOPY_PARSE_PLAN_TREES -DWRITE_READ_PARSE_PLAN_TREES -DRAW_EXPRESSION_COVERAGE_TEST
> to the FreeBSD ci task we now see the following:
>
> https://cirrus-ci.com/task/4772259058417664
> https://api.cirrus-ci.com/v1/artifact/task/4772259058417664/testrun/build/testrun/main/regress/regre...
>
> diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/updatable_views.out /tmp/cirrus-ci-build/build/testrun/main/regress/results/updatable_views.out
> --- /tmp/cirrus-ci-build/src/test/regress/expected/updatable_views.out  2022-10-02 10:37:08.888945000 +0000
> +++ /tmp/cirrus-ci-build/build/testrun/main/regress/results/updatable_views.out 2022-10-02 10:40:26.947887000 +0000
> @@ -1727,14 +1727,16 @@
>  (4 rows)
>
>  UPDATE base_tbl SET id = 2000 WHERE id = 2;
> +WARNING:  outfuncs/readfuncs failed to produce an equal rewritten parse tree
>  UPDATE rw_view1 SET id = 3000 WHERE id = 3;
> +WARNING:  outfuncs/readfuncs failed to produce an equal rewritten parse tree
>  SELECT * FROM base_tbl;
>    id  | idplus1
>  ------+---------
>      1 |       2
>      4 |       5
> - 2000 |    2001
> - 3000 |    3001
> + 2000 |       3
> + 3000 |       4
>  (4 rows)
>
>  DROP TABLE base_tbl CASCADE;
>
> and many more.

Thanks for the heads up.  Grateful for those new -D flags.

Turns out I had forgotten to update out/readRangeTblEntry() after
bringing extraUpdatedCols back into RangeTblEntry per Tom's comment.

Fixed in the attached.

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (2 files, .tar.gz)

v18-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 145.6K) download From 8df39257774986b761209545ef9fba30ed0a7182 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v18 1/2] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table looking for relations that need to have permissions checked. This can make the permission checking part of the executor initialization needlessly expensive when many inheritance children are present in the range range. While the permissions need not be checked on the individual child relations, the executor still must visit every range table entry to filter them out. This commit moves the permission checking information out of the range table entries into a new plan node called RTEPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RTEPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the correponding RTEPermissionInfo's index in the query's list of the latter. --- contrib/postgres_fdw/postgres_fdw.c | 82 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 12 +- contrib/sepgsql/sepgsql.h | 3 +- src/backend/access/common/attmap.c | 14 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 17 +- src/backend/commands/copyfrom.c | 11 +- src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 115 +++++------ src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 133 +++++++++---- src/backend/nodes/outfuncs.c | 6 +- src/backend/nodes/readfuncs.c | 6 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 43 +++- src/backend/optimizer/plan/subselect.c | 7 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 155 +++++++++++---- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 65 +++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 175 +++++++++-------- src/backend/parser/parse_target.c | 18 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 11 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 27 +-- src/backend/rewrite/rewriteHandler.c | 183 ++++++++---------- src/backend/rewrite/rewriteManip.c | 25 +++ src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 19 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 11 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 95 +++++---- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 9 +- src/include/parser/parse_relation.h | 4 + src/include/rewrite/rewriteHandler.h | 1 + src/include/rewrite/rewriteManip.h | 2 + .../modules/test_oat_hooks/test_oat_hooks.c | 12 +- 54 files changed, 951 insertions(+), 563 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index dd858aba03..9f233c37c4 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,36 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1935,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1943,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1967,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1979,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2147,6 +2183,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2224,6 +2261,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2240,7 +2279,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2626,7 +2666,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2646,13 +2685,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3955,12 +3993,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3972,12 +4010,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..c4e071b0ea 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -23,6 +23,7 @@ #include "commands/tablecmds.h" #include "executor/executor.h" #include "nodes/bitmapset.h" +#include "parser/parsetree.h" #include "sepgsql.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -277,38 +278,33 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, + bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, rtepermlist) { - RangeTblEntry *rte = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +335,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 87fdd972c2..129442b96e 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -35,7 +35,7 @@ PG_MODULE_MAGIC; * Saved hook entries (if stacked) */ static object_access_hook_type next_object_access_hook = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *rangeTbls, List *rtepermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (rangeTbls, rtepermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(rangeTbls, rtepermlist, abort)) return false; return true; @@ -471,8 +471,8 @@ _PG_init(void) object_access_hook = sepgsql_object_access; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = sepgsql_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = sepgsql_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f2a2c795bf..7aa6df92ec 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object, /* * dml.c */ -extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation); +extern bool sepgsql_dml_privileges(List *rangeTabls, List *rtepermlist, + bool abort_on_violation); /* * database.c diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..1e65d8a120 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,15 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error; AttrMap.attnums[] entry for such an + * outdesc column will be 0 in that case. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +240,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +262,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 49924e476a..5a62d5641d 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +155,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +177,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 175aa837f2..575ac5c81d 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the RTEPermissionInfos into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_rtepermlist = cstate->rtepermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1380,9 +1386,12 @@ BeginCopyFrom(ParseState *pstate, initStringInfo(&cstate->attribute_buf); - /* Assign range table, we'll need it in CopyFrom. */ + /* Assign range table and rtepermlist, we'll need them in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->rtepermlist = pstate->p_rtepermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index fd56066c13..622b574860 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1288,7 +1288,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7d8a75d23c..4e617892d8 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1206,7 +1206,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9647,7 +9648,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9814,7 +9816,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10020,7 +10023,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10198,7 +10202,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12303,7 +12308,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18024,7 +18030,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18951,7 +18958,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index b5a0fc02e5..6f07ac2a9c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -411,10 +411,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d78862e660..c43d2215b3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,8 +74,8 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ -ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; +/* Hook for plugin to get control in ExecCheckPermissions() */ +ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook = NULL; /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +565,63 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rtepermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } - if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, - ereport_on_violation); + if (ExecutorCheckPermissions_hook) + result = (*ExecutorCheckPermissions_hook) (rangeTable, rtepermlist, + ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->rtepermlist, true); + estate->es_rtepermlist = plannedstmt->rtepermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 99512826c5..c1c5439fa1 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->rtepermlist = estate->es_rtepermlist; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 40e3c07693..b7b57ec404 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -780,7 +782,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -878,7 +881,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1140,7 +1144,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..461230b011 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent, something that's allowed with + * traditional inheritance, are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRTEPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RTEPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RTEPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,41 +1318,64 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 64c65f060b..b91e235423 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -504,6 +504,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -557,11 +558,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b4ff855f7c..75bf11c741 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -468,6 +468,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -531,11 +532,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ab4d8e201d..f854855951 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5794,7 +5797,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 8014d1fd25..d03fcdf2fd 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrtepermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrtepermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -520,6 +523,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->rtepermlist = glob->finalrtepermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6209,6 +6213,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6336,6 +6341,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..a26aa36048 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -355,6 +355,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rtepermlist. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -370,14 +373,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ + rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) + { + /* + * Update perminfoindex, if any, to reflect the correponding + * RTEPermissionInfo's position in the flattened list. + */ + if (rte->perminfoindex > 0) + rte->perminfoindex += list_length(glob->finalrtepermlist); + add_rte_to_flat_rtable(glob, rte); + } + + rti++; } + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + root->parse->rtepermlist); + /* * If there are any dead subqueries, they are not referenced in the Plan * tree, so we must add RTEs contained in them to the flattened rtable @@ -450,6 +468,15 @@ flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) flatten_rtes_walker, (void *) glob, QTW_EXAMINE_RTES_BEFORE); + + /* + * Now add the subquery's RTEPermissionInfos too. flatten_rtes_walker() + * should already have updated the perminfoindex in the RTEs in the + * subquery to reflect the corresponding RTEPermissionInfos' position in + * finalrtepermlist. + */ + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + rte->subquery->rtepermlist); } static bool @@ -463,7 +490,15 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) + { + /* + * The correponding RTEPermissionInfo will get added to + * finalrtepermlist, so adjust perminfoindex accordingly. + */ + Assert(rte->perminfoindex > 0); + rte->perminfoindex += list_length(glob->finalrtepermlist); add_rte_to_flat_rtable(glob, rte); + } return false; } if (IsA(node, Query)) @@ -483,10 +518,10 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo for executor-startup + * permission checking. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..a61082d27c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,6 +1496,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, + subselect->rtable); + /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 41c7066d90..607f7ae907 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1205,6 +1198,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, + subquery->rtable); + /* * Now append the adjusted rtable entries to upper query. (We hold off * until after fixing the upper rtable entries; no point in running that @@ -1345,6 +1345,12 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, + subquery->rtepermlist, rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index cf7691a474..10c2aa13f6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *root_perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +312,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +332,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains the root partrel's + * updatedCols translated to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +409,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +468,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +491,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,33 +553,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ + /* Translate the bitmapset of generated columns being updated. */ if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); - } else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which @@ -866,3 +859,83 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * We need to get the updatedCols bitmapset from the relation's + * RTEPermissionInfo. + * + * If it's a simple "base" rel, can just fetch its RTEPermissionInfo that + * must always be present; this cannot get called on non-RELATION RTEs. + * + * For "other" rels, must look up the root parent relation mentioned in the + * query, because only that one gets assigned a RTEPermissionInfo, and + * translate the columns found therein to match the given relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + Assert(rte->perminfoindex > 0); + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + if (rel->top_parent_relids != NULL) + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + else + updatedCols = perminfo->updatedCols; + + /* extraUpdatedCols can be obtained directly from the RTE. */ + rte = planner_rt_fetch(rel->relid, root); + extraUpdatedCols = rte->extraUpdatedCols; + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index edcdd0a360..7711075ac2 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though + * only the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..25324d9486 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; + List *sub_rtepermlist; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -594,10 +596,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing - * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in - * that case we want the rule's OLD and NEW rtable entries to appear as - * part of the SELECT's rtable, not as outer references for it. (Kluge!) + * INSERT/SELECT, arrange to pass the rangetable/rtepermlist/namespace down + * to the SELECT. This can only happen if we are inside a CREATE RULE, + * and in that case we want the rule's OLD and NEW rtable entries to appear + * as part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ @@ -605,6 +607,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; + sub_rtepermlist = pstate->p_rtepermlist; + pstate->p_rtepermlist = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } @@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_rtepermlist = sub_rtepermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +899,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +915,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1102,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2432,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2444,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2485,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3252,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3341,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 202a38f813..77648263f7 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3224,16 +3224,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RTEPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..5781a4b995 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 81f9ae2f02..4dc8d7ecf5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,10 +1021,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1238,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RTEPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1280,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1424,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1461,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1470,8 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1485,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1522,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1546,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1555,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1570,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1644,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1974,20 +1969,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1999,7 +1987,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2066,20 +2054,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2154,19 +2135,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2251,19 +2226,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2278,6 +2247,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2401,19 +2371,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2527,16 +2491,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2548,7 +2509,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3173,6 +3134,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3190,7 +3152,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3742,3 +3707,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRTEPermissionInfo + * Creates RTEPermissionInfo for a given RTE and adds it into the + * provided list + * + * Returns the RTEPermissionInfo and sets rte->perminfoindex. + */ +RTEPermissionInfo * +AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex == 0); + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RTEPermissionInfo); + perminfo->relid = rte->relid; + perminfo->inh = rte->inh; + /* Other information is set by fetching the node as and where needed. */ + + *rtepermlist = lappend(*rtepermlist, perminfo); + + /* Note its index. */ + rte->perminfoindex = list_length(*rtepermlist); + + return perminfo; +} + +/* + * GetRTEPermissionInfo + * Find RTEPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RTEPermissionInfo * +GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + rte->perminfoindex, rte->relid); + perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL); + if (perminfo->relid != rte->relid) + elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bd8057bc3e..0415fcec7e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1132,7 +1132,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1367,6 +1367,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1381,7 +1382,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1414,11 +1418,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * with its RTEPermissionInfo's selectedCols bitmap showing the whole row + * as needing select permission, as well as the individual columns. + * However, we can only get here for weird notations like (table.*).*, so + * it's not worth trying to clean up --- arguably, the permissions marking + * is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index bd068bba05..f542b43549 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1232,7 +1232,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3022,9 +3023,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3080,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->rtepermlist = pstate->p_rtepermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3122,8 +3121,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 207a5805ba..e834130151 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -492,6 +493,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRTEPermissionInfo(&estate->es_rtepermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1789,6 +1792,7 @@ apply_handle_update(StringInfo s) bool has_oldtup; TupleTableSlot *remoteslot; RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1837,6 +1841,7 @@ apply_handle_update(StringInfo s) * on the subscriber, since we are not touching those. */ target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1846,14 +1851,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2ecaa5b907..f2128190d8 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1125,7 +1125,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 213eabfbb9..5e9d226e54 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -785,14 +785,14 @@ 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 rtable entries. + * 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 will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * 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) @@ -819,18 +819,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RTEPermissionInfos for this query. */ + foreach(l, qry->rtepermlist) + { + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d02fd83c0a..7dbbbc629b 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -352,6 +352,7 @@ rewriteRuleAction(Query *parsetree, Query *sub_action; Query **sub_action_ptr; acquireLocksOnSubLinks_context context; + List *query_rtable; context.for_execute = true; @@ -394,32 +395,35 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. + * Also merge RTEPermissionInfo lists to ensure that all permissions are + * checked correctly. * * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. - * - * NOTE: because planner will destructively alter rtable, we must ensure - * that rule action's rtable is separate and shares no substructure with - * the main rtable. Hence do a deep copy here. + * NOTE: because planner will destructively alter rtable and rtepermlist, + * we must ensure that rule action's lists are separate and shares no + * substructure with the main query's lists. Hence do a deep copy here + * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), + * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtable = list_concat(copyObject(parsetree->rtable), - sub_action->rtable); + sub_action->rtepermlist = copyObject(sub_action->rtepermlist); + query_rtable = copyObject(parsetree->rtable); + ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, + parsetree->rtepermlist, query_rtable); + sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1590,10 +1594,13 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; @@ -1616,7 +1623,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RTEPermissionInfo *view_perminfo; + RTEPermissionInfo *base_perminfo; + RTEPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3175,6 +3151,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); Assert(base_rte->rtekind == RTE_RELATION); + base_perminfo = GetRTEPermissionInfo(viewquery->rtepermlist, base_rte); /* * Up to now, the base relation hasn't been touched at all in our query. @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RTEPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRTEPermissionInfo(&parsetree->rtepermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 101c39553a..3552a8db59 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1531,3 +1531,28 @@ ReplaceVarsFromTargetList(Node *node, (void *) &context, outer_hasSubLinks); } + +/* + * ConcatRTEPermissionInfoLists + * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * + * Also updates perminfoindex of the RTEs in src_rtable to point to the + * "source" perminfos after they have been added into *dest_rtepermlist. + */ +void +ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable) +{ + ListCell *l; + int offset = list_length(*dest_rtepermlist); + + *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + + foreach(l, src_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (rte->perminfoindex > 0) + rte->perminfoindex += offset; + } +} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b2a7237430..e4ce49d606 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RTEPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRTEPermissionInfo(root->rtepermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ab97e71dd7..baf8c542b8 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1598,6 +1599,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo; int clause_relid; Oid userid; @@ -1646,10 +1648,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); /* Table-level SELECT privilege is sufficient for all columns */ if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 1d503e7e01..1d4611fb94 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1376,6 +1376,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; + RTEPermissionInfo *pk_perminfo; + RTEPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1398,27 +1400,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; + + pk_perminfo = makeNode(RTEPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RTEPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fkrte, pkrte), + list_make2(fk_perminfo, pk_perminfo), false)) return false; /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 1808388397..14a54eae53 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5171,7 +5171,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5223,7 +5223,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5302,7 +5302,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5350,7 +5350,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5411,6 +5411,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5419,7 +5420,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5488,7 +5489,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 00dc0f2403..7c439a3cfb 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RTEPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index e37c6032ae..4fbb8801ff 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rtepermlist; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ed95ed1176..89b10ee909 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,9 +80,9 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); -extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPermissions_hook_type) (List *, List *, bool); +extern PGDLLIMPORT ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook; /* @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rtepermlist, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..c32834a9e8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rtepermlist; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 633e7671b3..080680ecd0 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -152,6 +152,9 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -965,37 +968,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1051,11 +1023,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1175,14 +1152,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 294cfe9c47..478353e26f 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrtepermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 21e642a64c..aaff24256e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -72,6 +72,10 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -703,6 +707,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* user to perform the scan as */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..69665aba41 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rtepermlist: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rtepermlist; /* list of RTEPermissionInfo nodes for + * each RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rtepermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rtepermlist entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 484db165db..3cf475513b 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,9 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RTEPermissionInfo *AddRTEPermissionInfo(List **rtepermlist, + RangeTblEntry *rte); +extern RTEPermissionInfo *GetRTEPermissionInfo(List *rtepermlist, + RangeTblEntry *rte); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..05c3680cd6 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index f001ca41bb..0379dd9673 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,5 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); +extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable); #endif /* REWRITEMANIP_H */ diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c index 4b4e259cd2..bfa9263233 100644 --- a/src/test/modules/test_oat_hooks/test_oat_hooks.c +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -46,7 +46,7 @@ static bool REGRESS_suset_variable2 = false; /* Saved hook values */ static object_access_hook_type next_object_access_hook = NULL; static object_access_hook_type_str next_object_access_hook_str = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* Test Object Access Type Hook hooks */ @@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access, int subId, void *arg); static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg); -static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static bool REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort); static void REGRESS_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, @@ -219,8 +219,8 @@ _PG_init(void) object_access_hook_str = REGRESS_object_access_hook_str; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = REGRESS_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; @@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i } static bool -REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort) { bool am_super = superuser_arg(GetUserId()); bool allow = true; @@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) /* Forward to next hook in the chain */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + !(*next_exec_check_perms_hook) (rangeTabls, rtepermlist, do_abort)) allow = false; if (allow) -- 2.35.3
v18-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 120.6K) download From b9416f7bc74e0fbb80e1644b1ccc44bf968c3746 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v18 2/2] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RTEPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add a copy of the original view RTE. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteDefine.c | 7 - src/backend/rewrite/rewriteHandler.c | 33 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 222 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 27 files changed, 729 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 2e4e82a94f..cfe37815f0 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2606,7 +2606,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2669,7 +2669,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6552,10 +6552,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6669,10 +6669,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b0747ce291..1d5f30443b 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 6f07ac2a9c..7e3d5e79bc 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,78 +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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -587,12 +515,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 5e9d226e54..ac2568e59a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -786,13 +786,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 7dbbbc629b..156c033bda 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before converting the RTE to become a SUBQUERY, store a copy for the + * executor to be able to lock the view relation and for the planner to be + * able to record the view relation OID in PlannedStmt.relationOids. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1858,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index a869321cdf..934a731205 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2225,7 +2225,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2241,7 +2241,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2257,7 +2257,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2273,7 +2273,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2291,7 +2291,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3283,7 +3283,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index b2198724e3..4a55e42dd2 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1622,7 +1622,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1674,7 +1674,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1690,7 +1690,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1706,7 +1706,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2191,15 +2191,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 346f594ad0..ecf4f65c12 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2479,8 +2479,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2491,8 +2491,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2518,8 +2518,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2531,8 +2531,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index bf4ff30d86..8f81a3098e 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1623,11 +1623,11 @@ returning pg_describe_object(classid, objid, objsubid) as obj, alter table tt14t drop column f3; -- column f3 is still in the view, sort of ... select pg_get_viewdef('tt14v', true); - pg_get_viewdef ---------------------------------- - SELECT t.f1, + - t."?dropped?column?" AS f3,+ - t.f4 + + pg_get_viewdef +------------------------------- + SELECT f1, + + "?dropped?column?" AS f3,+ + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1675,9 +1675,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1697,8 +1697,8 @@ create view tt14v as select t.f1, t.f4 from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1712,8 +1712,8 @@ alter table tt14t drop column f3; -- ok select pg_get_viewdef('tt14v', true); pg_get_viewdef ---------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1806,8 +1806,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -2054,7 +2054,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2106,19 +2106,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e2e62db6a2..fbb840e848 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 9dd137415e..19ef0a6b80 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1448,10 +1448,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1697,23 +1697,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1727,10 +1727,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1798,13 +1798,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1817,57 +1817,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1890,8 +1890,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1900,13 +1900,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2016,16 +2016,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2101,41 +2101,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2145,68 +2145,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2223,19 +2223,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2245,19 +2245,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2301,64 +2301,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2543,24 +2543,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3080,7 +3080,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3119,8 +3119,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3132,8 +3132,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3145,8 +3145,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3158,8 +3158,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 8b8eadd181..019c4726f6 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 55dcd668c9..ddaefdd91d 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7f2e32d8b0..f40197acbf 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 948b4e702c..cc213523c0 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 5fd3886b5e..3986fc1706 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-04 03:45  Amit Langote <amitlangote09@gmail.com>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-10-04 03:45 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Jul 28, 2022 at 6:18 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> ... One more thing: maybe we should rethink where to put
> extraUpdatedCols.  Between the facts that it's not used for
> actual permissions checks, and that it's calculated by the
> rewriter not parser, it doesn't seem like it really belongs
> in RelPermissionInfo.  Should we keep it in RangeTblEntry?
> Should it go somewhere else entirely?  I'm just speculating,
> but now is a good time to think about it.

After fixing the issue related to this mentioned by Andres, I started
thinking more about this, especially the "Should it go somewhere else
entirely?" part.

I've kept extraUpdatedCols in RangeTblEntry in the latest patch, but
perhaps it makes sense to put that into Query?  So, the stanza in
RewriteQuery() that sets extraUpdatedCols will be changed as:

@@ -3769,7 +3769,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
                                    NULL, 0, NULL);

            /* Also populate extraUpdatedCols (for generated columns) */
-           fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation);
+           parsetree->extraUpdatedCols =
+               get_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation);
        }
        else if (event == CMD_MERGE)
        {

Then, like withCheckOptionLists et al, have grouping_planner()
populate an extraUpdatedColsBitmaps in ModifyTable.
ExecInitModifyTable() will assign them directly into ResultRelInfos.
That way, anyplace in the executor that needs to look at
extraUpdatedCols of a given result relation can get that from its
ResultRelInfo, rather than from the RangeTblEntry as now.

Thoughts?


--
Thanks, Amit Langote
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-04 03:54  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Tom Lane @ 2022-10-04 03:54 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Amit Langote <amitlangote09@gmail.com> writes:
> On Thu, Jul 28, 2022 at 6:18 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> ... One more thing: maybe we should rethink where to put
>> extraUpdatedCols.  Between the facts that it's not used for
>> actual permissions checks, and that it's calculated by the
>> rewriter not parser, it doesn't seem like it really belongs
>> in RelPermissionInfo.  Should we keep it in RangeTblEntry?
>> Should it go somewhere else entirely?  I'm just speculating,
>> but now is a good time to think about it.

> I've kept extraUpdatedCols in RangeTblEntry in the latest patch, but
> perhaps it makes sense to put that into Query?

That's got morally the same problem as keeping it in RangeTblEntry:
those are structures that are built by the parser.  Hacking on them
later isn't terribly clean.

I wonder if it could make sense to postpone calculation of the
extraUpdatedCols out of the rewriter and into the planner, with
the idea that it ends up $someplace in the finished plan tree
but isn't part of the original parsetree.

A different aspect of this is that putting it in Query doesn't
make a lot of sense unless there is only one version of the
bitmap per Query.  In simple UPDATEs that would be true, but
I think that inherited/partitioned UPDATEs would need one per
result relation, which is likely the reason it got dumped in
RangeTblEntry to begin with.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-04 04:11  Amit Langote <amitlangote09@gmail.com>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-10-04 04:11 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Oct 4, 2022 at 12:54 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Amit Langote <amitlangote09@gmail.com> writes:
> > On Thu, Jul 28, 2022 at 6:18 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> >> ... One more thing: maybe we should rethink where to put
> >> extraUpdatedCols.  Between the facts that it's not used for
> >> actual permissions checks, and that it's calculated by the
> >> rewriter not parser, it doesn't seem like it really belongs
> >> in RelPermissionInfo.  Should we keep it in RangeTblEntry?
> >> Should it go somewhere else entirely?  I'm just speculating,
> >> but now is a good time to think about it.
>
> > I've kept extraUpdatedCols in RangeTblEntry in the latest patch, but
> > perhaps it makes sense to put that into Query?
>
> That's got morally the same problem as keeping it in RangeTblEntry:
> those are structures that are built by the parser.  Hacking on them
> later isn't terribly clean.
>
> I wonder if it could make sense to postpone calculation of the
> extraUpdatedCols out of the rewriter and into the planner, with
> the idea that it ends up $someplace in the finished plan tree
> but isn't part of the original parsetree.

Looking at PlannerInfo.update_colnos, something that's needed for
execution but not in Query, maybe we can make preprocess_targetlist()
also populate an PlannerInfo.extraUpdatedCols?

> A different aspect of this is that putting it in Query doesn't
> make a lot of sense unless there is only one version of the
> bitmap per Query.  In simple UPDATEs that would be true, but
> I think that inherited/partitioned UPDATEs would need one per
> result relation, which is likely the reason it got dumped in
> RangeTblEntry to begin with.

Yeah, so if we have PlannerInfos.extraUpdatedCols as the root table's
version of that, grouping_planner() can make copies for all result
relations and put the list in ModifyTable.

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-06 13:29  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 3 replies; 97+ messages in thread

From: Amit Langote @ 2022-10-06 13:29 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Tue, Oct 4, 2022 at 1:11 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Tue, Oct 4, 2022 at 12:54 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > Amit Langote <amitlangote09@gmail.com> writes:
> > > On Thu, Jul 28, 2022 at 6:18 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > >> ... One more thing: maybe we should rethink where to put
> > >> extraUpdatedCols.  Between the facts that it's not used for
> > >> actual permissions checks, and that it's calculated by the
> > >> rewriter not parser, it doesn't seem like it really belongs
> > >> in RelPermissionInfo.  Should we keep it in RangeTblEntry?
> > >> Should it go somewhere else entirely?  I'm just speculating,
> > >> but now is a good time to think about it.
> >
> > > I've kept extraUpdatedCols in RangeTblEntry in the latest patch, but
> > > perhaps it makes sense to put that into Query?
> >
> > That's got morally the same problem as keeping it in RangeTblEntry:
> > those are structures that are built by the parser.  Hacking on them
> > later isn't terribly clean.
> >
> > I wonder if it could make sense to postpone calculation of the
> > extraUpdatedCols out of the rewriter and into the planner, with
> > the idea that it ends up $someplace in the finished plan tree
> > but isn't part of the original parsetree.
>
> Looking at PlannerInfo.update_colnos, something that's needed for
> execution but not in Query, maybe we can make preprocess_targetlist()
> also populate an PlannerInfo.extraUpdatedCols?
>
> > A different aspect of this is that putting it in Query doesn't
> > make a lot of sense unless there is only one version of the
> > bitmap per Query.  In simple UPDATEs that would be true, but
> > I think that inherited/partitioned UPDATEs would need one per
> > result relation, which is likely the reason it got dumped in
> > RangeTblEntry to begin with.
>
> Yeah, so if we have PlannerInfos.extraUpdatedCols as the root table's
> version of that, grouping_planner() can make copies for all result
> relations and put the list in ModifyTable.

I tried in the attached 0004.  ModifyTable gets a new member
extraUpdatedColsBitmaps, which is List of Bitmapset "nodes".

Actually, List of Bitmapsets turned out to be something that doesn't
just-work with our Node infrastructure, which I found out thanks to
-DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
first-class support for copy/equal/write/read support for Bitmapsets,
such that writeNode() can write appropriately labeled versions of them
and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
didn't actually go ahead and make *all* Bitmapsets in the plan trees
to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
just use *_NODE_FIELD().

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (4 files, .tar.gz)

v19-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 120.6K) download From e9925de0df0a5803c75cfc4b081d93404708d03d Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v19 2/4] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RTEPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add a copy of the original view RTE. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteDefine.c | 7 - src/backend/rewrite/rewriteHandler.c | 33 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 222 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 27 files changed, 729 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index cc9e39c4a5..b6c3749395 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2606,7 +2606,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2669,7 +2669,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6555,10 +6555,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6672,10 +6672,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b0747ce291..1d5f30443b 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 6f07ac2a9c..7e3d5e79bc 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,78 +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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -587,12 +515,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 5e9d226e54..ac2568e59a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -786,13 +786,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 7dbbbc629b..156c033bda 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before converting the RTE to become a SUBQUERY, store a copy for the + * executor to be able to lock the view relation and for the planner to be + * able to record the view relation OID in PlannedStmt.relationOids. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1858,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index a869321cdf..934a731205 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2225,7 +2225,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2241,7 +2241,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2257,7 +2257,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2273,7 +2273,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2291,7 +2291,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3283,7 +3283,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index fc2bd40be2..564a7ba1aa 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1623,7 +1623,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1675,7 +1675,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1691,7 +1691,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1707,7 +1707,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2192,15 +2192,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 346f594ad0..ecf4f65c12 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2479,8 +2479,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2491,8 +2491,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2518,8 +2518,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2531,8 +2531,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index bf4ff30d86..8f81a3098e 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1623,11 +1623,11 @@ returning pg_describe_object(classid, objid, objsubid) as obj, alter table tt14t drop column f3; -- column f3 is still in the view, sort of ... select pg_get_viewdef('tt14v', true); - pg_get_viewdef ---------------------------------- - SELECT t.f1, + - t."?dropped?column?" AS f3,+ - t.f4 + + pg_get_viewdef +------------------------------- + SELECT f1, + + "?dropped?column?" AS f3,+ + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1675,9 +1675,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1697,8 +1697,8 @@ create view tt14v as select t.f1, t.f4 from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1712,8 +1712,8 @@ alter table tt14t drop column f3; -- ok select pg_get_viewdef('tt14v', true); pg_get_viewdef ---------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1806,8 +1806,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -2054,7 +2054,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2106,19 +2106,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e2e62db6a2..fbb840e848 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 9dd137415e..19ef0a6b80 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1448,10 +1448,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1697,23 +1697,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1727,10 +1727,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1798,13 +1798,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1817,57 +1817,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1890,8 +1890,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1900,13 +1900,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2016,16 +2016,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2101,41 +2101,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2145,68 +2145,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2223,19 +2223,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2245,19 +2245,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2301,64 +2301,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2543,24 +2543,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3080,7 +3080,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3119,8 +3119,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3132,8 +3132,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3145,8 +3145,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3158,8 +3158,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 8b8eadd181..019c4726f6 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 55dcd668c9..ddaefdd91d 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7f2e32d8b0..f40197acbf 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 948b4e702c..cc213523c0 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 5fd3886b5e..3986fc1706 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
v19-0003-Allow-adding-Bitmapsets-as-Nodes-into-plan-trees.patch (application/octet-stream, 4.6K) download From 16a776bf9a4106acbd7b614e54a60d062eb132f3 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Thu, 6 Oct 2022 17:31:37 +0900 Subject: [PATCH v19 3/4] Allow adding Bitmapsets as Nodes into plan trees Note that this only adds some infrastructure bits and none of the existing bitmapsets that are added to plan trees have been changed to instead add the Node version. So, the plan trees, or really the bitmapsets contained in them, look the same as before as far as Node write/read functionality is concerned. This is needed, because it is not currently possible to write and then read back Bitmapsets that are not direct members of write/read capable Nodes; for example, if one needs to add a List of Bitmapsets to a plan tree. The most straightforward way to do that is to make Bitmapsets be written with outNode() and read with nodeRead(). --- src/backend/nodes/Makefile | 3 ++- src/backend/nodes/copyfuncs.c | 11 +++++++++++ src/backend/nodes/equalfuncs.c | 6 ++++++ src/backend/nodes/gen_node_support.pl | 1 + src/backend/nodes/outfuncs.c | 11 +++++++++++ src/backend/optimizer/prep/preptlist.c | 1 - src/include/nodes/bitmapset.h | 5 +++++ 7 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile index 7450e191ee..da5307771b 100644 --- a/src/backend/nodes/Makefile +++ b/src/backend/nodes/Makefile @@ -57,7 +57,8 @@ node_headers = \ nodes/replnodes.h \ nodes/supportnodes.h \ nodes/value.h \ - utils/rel.h + utils/rel.h \ + nodes/bitmapset.h # see also catalog/Makefile for an explanation of these make rules diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e76fda8eba..1482019327 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -160,6 +160,17 @@ _copyExtensibleNode(const ExtensibleNode *from) return newnode; } +/* Custom copy routine for Node bitmapsets */ +static Bitmapset * +_copyBitmapset(const Bitmapset *from) +{ + Bitmapset *newnode = bms_copy(from); + + newnode->type = T_Bitmapset; + + return newnode; +} + /* * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 0373aa30fe..e8706c461a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -210,6 +210,12 @@ _equalList(const List *a, const List *b) return true; } +/* Custom equal routine for Node bitmapsets */ +static bool +_equalBitmapset(const Bitmapset *a, const Bitmapset *b) +{ + return bms_equal(a, b); +} /* * equal diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index 81b8c184a9..ccb5aff874 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -71,6 +71,7 @@ my @all_input_files = qw( nodes/supportnodes.h nodes/value.h utils/rel.h + nodes/bitmapset.h ); # Nodes from these input files are automatically treated as nodetag_only. diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b91e235423..d3beb907ea 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -328,6 +328,17 @@ outBitmapset(StringInfo str, const Bitmapset *bms) appendStringInfoChar(str, ')'); } +/* Custom write routine for Node bitmapsets */ +static void +_outBitmapset(StringInfo str, const Bitmapset *bms) +{ + Assert(IsA(bms, Bitmapset)); + WRITE_NODE_TYPE("BITMAPSET"); + + outBitmapset(str, bms); +} + + /* * Print the value of a Datum given its type. */ diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 137b28323d..e5c1103316 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -337,7 +337,6 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } - /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h index 75b5ce1a8e..9046ca177f 100644 --- a/src/include/nodes/bitmapset.h +++ b/src/include/nodes/bitmapset.h @@ -20,6 +20,8 @@ #ifndef BITMAPSET_H #define BITMAPSET_H +#include "nodes/nodes.h" + /* * Forward decl to save including pg_list.h */ @@ -48,6 +50,9 @@ typedef int32 signedbitmapword; /* must be the matching signed type */ typedef struct Bitmapset { + pg_node_attr(custom_copy_equal, custom_read_write) + + NodeTag type; int nwords; /* number of words in array */ bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */ } Bitmapset; -- 2.35.3
v19-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 145.6K) download From 616768f11da3b92b72645bcd399aa8cb64e9c1da Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v19 1/4] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table looking for relations that need to have permissions checked. This can make the permission checking part of the executor initialization needlessly expensive when many inheritance children are present in the range range. While the permissions need not be checked on the individual child relations, the executor still must visit every range table entry to filter them out. This commit moves the permission checking information out of the range table entries into a new plan node called RTEPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RTEPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the correponding RTEPermissionInfo's index in the query's list of the latter. --- contrib/postgres_fdw/postgres_fdw.c | 82 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 12 +- contrib/sepgsql/sepgsql.h | 3 +- src/backend/access/common/attmap.c | 14 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 17 +- src/backend/commands/copyfrom.c | 11 +- src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 115 +++++------ src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 133 +++++++++---- src/backend/nodes/outfuncs.c | 6 +- src/backend/nodes/readfuncs.c | 6 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 43 +++- src/backend/optimizer/plan/subselect.c | 7 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 155 +++++++++++---- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 65 +++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 175 +++++++++-------- src/backend/parser/parse_target.c | 18 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 11 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 27 +-- src/backend/rewrite/rewriteHandler.c | 183 ++++++++---------- src/backend/rewrite/rewriteManip.c | 25 +++ src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 19 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 11 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 95 +++++---- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 9 +- src/include/parser/parse_relation.h | 4 + src/include/rewrite/rewriteHandler.h | 1 + src/include/rewrite/rewriteManip.h | 2 + .../modules/test_oat_hooks/test_oat_hooks.c | 12 +- 54 files changed, 951 insertions(+), 563 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index dd858aba03..9f233c37c4 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,36 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1935,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1943,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1967,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1979,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2147,6 +2183,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2224,6 +2261,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2240,7 +2279,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2626,7 +2666,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2646,13 +2685,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3955,12 +3993,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3972,12 +4010,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..c4e071b0ea 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -23,6 +23,7 @@ #include "commands/tablecmds.h" #include "executor/executor.h" #include "nodes/bitmapset.h" +#include "parser/parsetree.h" #include "sepgsql.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -277,38 +278,33 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, + bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, rtepermlist) { - RangeTblEntry *rte = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +335,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 87fdd972c2..129442b96e 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -35,7 +35,7 @@ PG_MODULE_MAGIC; * Saved hook entries (if stacked) */ static object_access_hook_type next_object_access_hook = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *rangeTbls, List *rtepermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (rangeTbls, rtepermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(rangeTbls, rtepermlist, abort)) return false; return true; @@ -471,8 +471,8 @@ _PG_init(void) object_access_hook = sepgsql_object_access; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = sepgsql_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = sepgsql_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f2a2c795bf..7aa6df92ec 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object, /* * dml.c */ -extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation); +extern bool sepgsql_dml_privileges(List *rangeTabls, List *rtepermlist, + bool abort_on_violation); /* * database.c diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..1e65d8a120 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,15 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error; AttrMap.attnums[] entry for such an + * outdesc column will be 0 in that case. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +240,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +262,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 49924e476a..5a62d5641d 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +155,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +177,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 175aa837f2..575ac5c81d 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the RTEPermissionInfos into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_rtepermlist = cstate->rtepermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1380,9 +1386,12 @@ BeginCopyFrom(ParseState *pstate, initStringInfo(&cstate->attribute_buf); - /* Assign range table, we'll need it in CopyFrom. */ + /* Assign range table and rtepermlist, we'll need them in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->rtepermlist = pstate->p_rtepermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index fd56066c13..622b574860 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1288,7 +1288,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1f774ac065..c2f50ae215 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1206,7 +1206,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9647,7 +9648,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9814,7 +9816,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10020,7 +10023,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10198,7 +10202,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12303,7 +12308,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18024,7 +18030,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18951,7 +18958,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index b5a0fc02e5..6f07ac2a9c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -411,10 +411,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d78862e660..c43d2215b3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,8 +74,8 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ -ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; +/* Hook for plugin to get control in ExecCheckPermissions() */ +ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook = NULL; /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +565,63 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rtepermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } - if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, - ereport_on_violation); + if (ExecutorCheckPermissions_hook) + result = (*ExecutorCheckPermissions_hook) (rangeTable, rtepermlist, + ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->rtepermlist, true); + estate->es_rtepermlist = plannedstmt->rtepermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 99512826c5..c1c5439fa1 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->rtepermlist = estate->es_rtepermlist; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 40e3c07693..b7b57ec404 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -780,7 +782,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -878,7 +881,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1140,7 +1144,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..461230b011 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent, something that's allowed with + * traditional inheritance, are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRTEPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RTEPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RTEPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,41 +1318,64 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 64c65f060b..b91e235423 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -504,6 +504,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -557,11 +558,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b4ff855f7c..75bf11c741 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -468,6 +468,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -531,11 +532,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ab4d8e201d..f854855951 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5794,7 +5797,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 5d0fd6e072..9576b69f1a 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrtepermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrtepermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -520,6 +523,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->rtepermlist = glob->finalrtepermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6210,6 +6214,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6337,6 +6342,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..a26aa36048 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -355,6 +355,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rtepermlist. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -370,14 +373,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ + rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) + { + /* + * Update perminfoindex, if any, to reflect the correponding + * RTEPermissionInfo's position in the flattened list. + */ + if (rte->perminfoindex > 0) + rte->perminfoindex += list_length(glob->finalrtepermlist); + add_rte_to_flat_rtable(glob, rte); + } + + rti++; } + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + root->parse->rtepermlist); + /* * If there are any dead subqueries, they are not referenced in the Plan * tree, so we must add RTEs contained in them to the flattened rtable @@ -450,6 +468,15 @@ flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) flatten_rtes_walker, (void *) glob, QTW_EXAMINE_RTES_BEFORE); + + /* + * Now add the subquery's RTEPermissionInfos too. flatten_rtes_walker() + * should already have updated the perminfoindex in the RTEs in the + * subquery to reflect the corresponding RTEPermissionInfos' position in + * finalrtepermlist. + */ + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + rte->subquery->rtepermlist); } static bool @@ -463,7 +490,15 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) + { + /* + * The correponding RTEPermissionInfo will get added to + * finalrtepermlist, so adjust perminfoindex accordingly. + */ + Assert(rte->perminfoindex > 0); + rte->perminfoindex += list_length(glob->finalrtepermlist); add_rte_to_flat_rtable(glob, rte); + } return false; } if (IsA(node, Query)) @@ -483,10 +518,10 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo for executor-startup + * permission checking. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..a61082d27c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,6 +1496,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, + subselect->rtable); + /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 41c7066d90..607f7ae907 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1205,6 +1198,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, + subquery->rtable); + /* * Now append the adjusted rtable entries to upper query. (We hold off * until after fixing the upper rtable entries; no point in running that @@ -1345,6 +1345,12 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, + subquery->rtepermlist, rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index cf7691a474..10c2aa13f6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *root_perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +312,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +332,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains the root partrel's + * updatedCols translated to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +409,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +468,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +491,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,33 +553,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ + /* Translate the bitmapset of generated columns being updated. */ if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); - } else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which @@ -866,3 +859,83 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * We need to get the updatedCols bitmapset from the relation's + * RTEPermissionInfo. + * + * If it's a simple "base" rel, can just fetch its RTEPermissionInfo that + * must always be present; this cannot get called on non-RELATION RTEs. + * + * For "other" rels, must look up the root parent relation mentioned in the + * query, because only that one gets assigned a RTEPermissionInfo, and + * translate the columns found therein to match the given relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + Assert(rte->perminfoindex > 0); + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + if (rel->top_parent_relids != NULL) + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + else + updatedCols = perminfo->updatedCols; + + /* extraUpdatedCols can be obtained directly from the RTE. */ + rte = planner_rt_fetch(rel->relid, root); + extraUpdatedCols = rte->extraUpdatedCols; + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index edcdd0a360..7711075ac2 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though + * only the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..25324d9486 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; + List *sub_rtepermlist; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -594,10 +596,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing - * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in - * that case we want the rule's OLD and NEW rtable entries to appear as - * part of the SELECT's rtable, not as outer references for it. (Kluge!) + * INSERT/SELECT, arrange to pass the rangetable/rtepermlist/namespace down + * to the SELECT. This can only happen if we are inside a CREATE RULE, + * and in that case we want the rule's OLD and NEW rtable entries to appear + * as part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ @@ -605,6 +607,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; + sub_rtepermlist = pstate->p_rtepermlist; + pstate->p_rtepermlist = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } @@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_rtepermlist = sub_rtepermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +899,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +915,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1102,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2432,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2444,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2485,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3252,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3341,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index c2b5474f5f..95cfe7d2b5 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3224,16 +3224,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RTEPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..5781a4b995 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 81f9ae2f02..4dc8d7ecf5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,10 +1021,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1238,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RTEPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1280,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1424,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1461,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1470,8 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1485,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1522,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1546,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1555,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1570,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1644,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1974,20 +1969,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1999,7 +1987,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2066,20 +2054,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2154,19 +2135,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2251,19 +2226,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2278,6 +2247,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2401,19 +2371,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2527,16 +2491,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2548,7 +2509,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3173,6 +3134,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3190,7 +3152,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3742,3 +3707,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRTEPermissionInfo + * Creates RTEPermissionInfo for a given RTE and adds it into the + * provided list + * + * Returns the RTEPermissionInfo and sets rte->perminfoindex. + */ +RTEPermissionInfo * +AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex == 0); + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RTEPermissionInfo); + perminfo->relid = rte->relid; + perminfo->inh = rte->inh; + /* Other information is set by fetching the node as and where needed. */ + + *rtepermlist = lappend(*rtepermlist, perminfo); + + /* Note its index. */ + rte->perminfoindex = list_length(*rtepermlist); + + return perminfo; +} + +/* + * GetRTEPermissionInfo + * Find RTEPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RTEPermissionInfo * +GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + rte->perminfoindex, rte->relid); + perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL); + if (perminfo->relid != rte->relid) + elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bd8057bc3e..0415fcec7e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1132,7 +1132,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1367,6 +1367,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1381,7 +1382,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1414,11 +1418,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * with its RTEPermissionInfo's selectedCols bitmap showing the whole row + * as needing select permission, as well as the individual columns. + * However, we can only get here for weird notations like (table.*).*, so + * it's not worth trying to clean up --- arguably, the permissions marking + * is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index bd068bba05..f542b43549 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1232,7 +1232,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3022,9 +3023,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3080,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->rtepermlist = pstate->p_rtepermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3122,8 +3121,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 207a5805ba..e834130151 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -492,6 +493,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRTEPermissionInfo(&estate->es_rtepermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1789,6 +1792,7 @@ apply_handle_update(StringInfo s) bool has_oldtup; TupleTableSlot *remoteslot; RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1837,6 +1841,7 @@ apply_handle_update(StringInfo s) * on the subscriber, since we are not touching those. */ target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1846,14 +1851,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2ecaa5b907..f2128190d8 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1125,7 +1125,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 213eabfbb9..5e9d226e54 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -785,14 +785,14 @@ 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 rtable entries. + * 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 will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * 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) @@ -819,18 +819,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RTEPermissionInfos for this query. */ + foreach(l, qry->rtepermlist) + { + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d02fd83c0a..7dbbbc629b 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -352,6 +352,7 @@ rewriteRuleAction(Query *parsetree, Query *sub_action; Query **sub_action_ptr; acquireLocksOnSubLinks_context context; + List *query_rtable; context.for_execute = true; @@ -394,32 +395,35 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. + * Also merge RTEPermissionInfo lists to ensure that all permissions are + * checked correctly. * * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. - * - * NOTE: because planner will destructively alter rtable, we must ensure - * that rule action's rtable is separate and shares no substructure with - * the main rtable. Hence do a deep copy here. + * NOTE: because planner will destructively alter rtable and rtepermlist, + * we must ensure that rule action's lists are separate and shares no + * substructure with the main query's lists. Hence do a deep copy here + * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), + * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtable = list_concat(copyObject(parsetree->rtable), - sub_action->rtable); + sub_action->rtepermlist = copyObject(sub_action->rtepermlist); + query_rtable = copyObject(parsetree->rtable); + ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, + parsetree->rtepermlist, query_rtable); + sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1590,10 +1594,13 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; @@ -1616,7 +1623,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RTEPermissionInfo *view_perminfo; + RTEPermissionInfo *base_perminfo; + RTEPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3175,6 +3151,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); Assert(base_rte->rtekind == RTE_RELATION); + base_perminfo = GetRTEPermissionInfo(viewquery->rtepermlist, base_rte); /* * Up to now, the base relation hasn't been touched at all in our query. @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RTEPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRTEPermissionInfo(&parsetree->rtepermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 101c39553a..3552a8db59 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1531,3 +1531,28 @@ ReplaceVarsFromTargetList(Node *node, (void *) &context, outer_hasSubLinks); } + +/* + * ConcatRTEPermissionInfoLists + * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * + * Also updates perminfoindex of the RTEs in src_rtable to point to the + * "source" perminfos after they have been added into *dest_rtepermlist. + */ +void +ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable) +{ + ListCell *l; + int offset = list_length(*dest_rtepermlist); + + *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + + foreach(l, src_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (rte->perminfoindex > 0) + rte->perminfoindex += offset; + } +} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b2a7237430..e4ce49d606 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RTEPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRTEPermissionInfo(root->rtepermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ab97e71dd7..baf8c542b8 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1598,6 +1599,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo; int clause_relid; Oid userid; @@ -1646,10 +1648,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); /* Table-level SELECT privilege is sufficient for all columns */ if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 1d503e7e01..1d4611fb94 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1376,6 +1376,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; + RTEPermissionInfo *pk_perminfo; + RTEPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1398,27 +1400,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; + + pk_perminfo = makeNode(RTEPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RTEPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fkrte, pkrte), + list_make2(fk_perminfo, pk_perminfo), false)) return false; /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 234fb66580..e1065db5c7 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5139,7 +5139,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5191,7 +5191,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5270,7 +5270,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5318,7 +5318,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5379,6 +5379,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5387,7 +5388,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5456,7 +5457,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 00dc0f2403..7c439a3cfb 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RTEPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index e37c6032ae..4fbb8801ff 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rtepermlist; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ed95ed1176..89b10ee909 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,9 +80,9 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); -extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPermissions_hook_type) (List *, List *, bool); +extern PGDLLIMPORT ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook; /* @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rtepermlist, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..c32834a9e8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rtepermlist; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 633e7671b3..080680ecd0 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -152,6 +152,9 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -965,37 +968,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1051,11 +1023,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1175,14 +1152,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 6bda383bea..99c8d4f611 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrtepermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 21e642a64c..aaff24256e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -72,6 +72,10 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -703,6 +707,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* user to perform the scan as */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..69665aba41 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rtepermlist: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rtepermlist; /* list of RTEPermissionInfo nodes for + * each RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rtepermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rtepermlist entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 484db165db..3cf475513b 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,9 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RTEPermissionInfo *AddRTEPermissionInfo(List **rtepermlist, + RangeTblEntry *rte); +extern RTEPermissionInfo *GetRTEPermissionInfo(List *rtepermlist, + RangeTblEntry *rte); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..05c3680cd6 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index f001ca41bb..0379dd9673 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,5 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); +extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable); #endif /* REWRITEMANIP_H */ diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c index 4b4e259cd2..bfa9263233 100644 --- a/src/test/modules/test_oat_hooks/test_oat_hooks.c +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -46,7 +46,7 @@ static bool REGRESS_suset_variable2 = false; /* Saved hook values */ static object_access_hook_type next_object_access_hook = NULL; static object_access_hook_type_str next_object_access_hook_str = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* Test Object Access Type Hook hooks */ @@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access, int subId, void *arg); static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg); -static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static bool REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort); static void REGRESS_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, @@ -219,8 +219,8 @@ _PG_init(void) object_access_hook_str = REGRESS_object_access_hook_str; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = REGRESS_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; @@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i } static bool -REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort) { bool am_super = superuser_arg(GetUserId()); bool allow = true; @@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) /* Forward to next hook in the chain */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + !(*next_exec_check_perms_hook) (rangeTabls, rtepermlist, do_abort)) allow = false; if (allow) -- 2.35.3
v19-0004-Add-per-result-relation-extraUpdatedCols-to-Modi.patch (application/octet-stream, 27.7K) download From 4fe45ab83aa1aa406e67dd1ef48af98d8e5b8afc Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Tue, 4 Oct 2022 20:54:03 +0900 Subject: [PATCH v19 4/4] Add per-result-relation extraUpdatedCols to ModifyTable In spirit of removing things from RangeTblEntry that are better carried by Query or PlannedStmt directly. --- src/backend/executor/execUtils.c | 9 ++-- src/backend/executor/nodeModifyTable.c | 4 ++ src/backend/nodes/outfuncs.c | 1 - src/backend/nodes/readfuncs.c | 1 - src/backend/optimizer/plan/createplan.c | 6 ++- src/backend/optimizer/plan/planner.c | 17 +++++++ src/backend/optimizer/prep/preptlist.c | 49 ++++++++++++++++++ src/backend/optimizer/util/inherit.c | 64 +++++++++++++++--------- src/backend/optimizer/util/pathnode.c | 4 ++ src/backend/replication/logical/worker.c | 6 +-- src/backend/rewrite/rewriteHandler.c | 45 ----------------- src/include/nodes/execnodes.h | 3 ++ src/include/nodes/parsenodes.h | 1 - src/include/nodes/pathnodes.h | 3 ++ src/include/nodes/plannodes.h | 1 + src/include/optimizer/inherit.h | 3 ++ src/include/optimizer/pathnode.h | 1 + src/include/optimizer/prep.h | 4 ++ src/include/rewrite/rewriteHandler.h | 4 -- 19 files changed, 138 insertions(+), 88 deletions(-) diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 461230b011..ede4c98875 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1378,20 +1378,17 @@ ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { if (relinfo->ri_RangeTableIndex != 0) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + return relinfo->ri_extraUpdatedCols; } else if (relinfo->ri_RootResultRelInfo) { ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); if (relinfo->ri_RootToPartitionMap != NULL) return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); + rootRelInfo->ri_extraUpdatedCols); else - return rte->extraUpdatedCols; + return rootRelInfo->ri_extraUpdatedCols; } else return NULL; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 04454ad6e6..e034e3ceee 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3971,6 +3971,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) { resultRelInfo = &mtstate->resultRelInfo[i]; + if (node->extraUpdatedColsBitmaps) + resultRelInfo->ri_extraUpdatedCols = + list_nth(node->extraUpdatedColsBitmaps, i); + /* Let FDWs init themselves for foreign-table result rels */ if (!resultRelInfo->ri_usesFdwDirectModify && resultRelInfo->ri_FdwRoutine != NULL && diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index d3beb907ea..139d8e095f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -569,7 +569,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 75bf11c741..bcd380516d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -532,7 +532,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f854855951..5c1c7aed2f 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -308,7 +308,7 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); @@ -2824,6 +2824,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->partColsUpdated, best_path->resultRelations, best_path->updateColnosLists, + best_path->extraUpdatedColsBitmaps, best_path->withCheckOptionLists, best_path->returningLists, best_path->rowMarks, @@ -6980,7 +6981,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -7049,6 +7050,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, node->exclRelTlist = onconflict->exclRelTlist; } node->updateColnosLists = updateColnosLists; + node->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; node->withCheckOptionLists = withCheckOptionLists; node->returningLists = returningLists; node->rowMarks = rowMarks; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9576b69f1a..8913200879 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1746,6 +1746,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) Index rootRelation; List *resultRelations = NIL; List *updateColnosLists = NIL; + List *extraUpdatedColsBitmaps = NIL; List *withCheckOptionLists = NIL; List *returningLists = NIL; List *mergeActionLists = NIL; @@ -1779,15 +1780,24 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) if (parse->commandType == CMD_UPDATE) { List *update_colnos = root->update_colnos; + Bitmapset *extraUpdatedCols = root->extraUpdatedCols; if (this_result_rel != top_result_rel) + { update_colnos = adjust_inherited_attnums_multilevel(root, update_colnos, this_result_rel->relid, top_result_rel->relid); + extraUpdatedCols = + translate_col_privs_multilevel(root, resultRelation, + extraUpdatedCols, + this_result_rel->top_parent_relids); + } updateColnosLists = lappend(updateColnosLists, update_colnos); + extraUpdatedColsBitmaps = lappend(extraUpdatedColsBitmaps, + extraUpdatedCols); } if (parse->withCheckOptions) { @@ -1869,7 +1879,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1883,7 +1896,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) /* Single-relation INSERT/UPDATE/DELETE. */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1922,6 +1938,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) root->partColsUpdated, resultRelations, updateColnosLists, + extraUpdatedColsBitmaps, withCheckOptionLists, returningLists, rowMarks, diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index e5c1103316..10a948ed40 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -43,6 +43,7 @@ #include "optimizer/prep.h" #include "optimizer/tlist.h" #include "parser/parse_coerce.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "utils/rel.h" @@ -106,6 +107,17 @@ preprocess_targetlist(PlannerInfo *root) else if (command_type == CMD_UPDATE) root->update_colnos = extract_update_targetlist_colnos(tlist); + /* Also populate extraUpdatedCols (for generated columns) */ + if (command_type == CMD_UPDATE) + { + RTEPermissionInfo *target_perminfo = + GetRTEPermissionInfo(parse->rtepermlist, target_rte); + + root->extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, + target_relation); + } + /* * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s) * needed to allow the executor to identify the rows to be updated or @@ -337,6 +349,43 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } +/* + * Return the indexes of any generated columns that depend on any columns + * mentioned in target_perminfo->updatedCols. + */ +Bitmapset * +get_extraUpdatedCols(Bitmapset *updatedCols, Relation target_relation) +{ + TupleDesc tupdesc = RelationGetDescr(target_relation); + TupleConstr *constr = tupdesc->constr; + /* These go into ModifyTable.extraUpdatedColsBitmaps, so make as Nodes. */ + Bitmapset *extraUpdatedCols = makeNode(Bitmapset); + + if (constr && constr->has_generated_stored) + { + for (int i = 0; i < constr->num_defval; i++) + { + AttrDefault *defval = &constr->defval[i]; + Node *expr; + Bitmapset *attrs_used = NULL; + + /* skip if not generated column */ + if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) + continue; + + /* identify columns this generated column depends on */ + expr = stringToNode(defval->adbin); + pull_varattnos(expr, 1, &attrs_used); + + if (bms_overlap(updatedCols, attrs_used)) + extraUpdatedCols = bms_add_member(extraUpdatedCols, + defval->adnum - FirstLowInvalidHeapAttributeNumber); + } + } + + return extraUpdatedCols; +} + /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 10c2aa13f6..ea632a94a6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -40,6 +40,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -148,6 +149,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, expand_partitioned_rtentry(root, rel, rte, rti, oldrelation, root_perminfo->updatedCols, + root->extraUpdatedCols, oldrc, lockmode); } else @@ -313,6 +315,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -343,7 +346,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -412,14 +415,18 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, { AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; child_updatedCols = translate_col_privs(parent_updatedCols, appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, childrel, child_updatedCols, + child_extraUpdatedCols, top_parentrc, lockmode); } @@ -456,7 +463,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -486,7 +492,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else @@ -553,13 +559,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* Translate the bitmapset of generated columns being updated. */ - if (childOID != parentOID) - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - else - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -656,7 +655,8 @@ static Bitmapset * translate_col_privs(const Bitmapset *parent_privs, List *translated_vars) { - Bitmapset *child_privs = NULL; + /* These go into ModifyTable.extraUpdatedColsBitmaps, so make as Nodes. */ + Bitmapset *child_privs = makeNode(Bitmapset); bool whole_row; int attno; ListCell *lc; @@ -866,13 +866,16 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, * 'top_parent_cols' to the columns numbers of a descendent relation * given by 'relid' */ -static Bitmapset * -translate_col_privs_recurse(PlannerInfo *root, Index relid, - Bitmapset *top_parent_cols, - Relids top_parent_relids) +Bitmapset * +translate_col_privs_multilevel(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) { AppendRelInfo *appinfo; + if (top_parent_cols == NULL) + return NULL; + Assert(root->append_rel_array != NULL); appinfo = root->append_rel_array[relid]; Assert(appinfo != NULL); @@ -883,9 +886,9 @@ translate_col_privs_recurse(PlannerInfo *root, Index relid, * and child relation pairs. */ if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) - translate_col_privs_recurse(root, appinfo->parent_relid, - top_parent_cols, - top_parent_relids); + translate_col_privs_multilevel(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); return translate_col_privs(top_parent_cols, appinfo->translated_vars); } @@ -903,6 +906,8 @@ GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) Bitmapset *updatedCols, *extraUpdatedCols; + Assert(root->parse->commandType == CMD_UPDATE); + if (!IS_SIMPLE_REL(rel)) return NULL; @@ -918,24 +923,33 @@ GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) * translate the columns found therein to match the given relation. */ if (rel->top_parent_relids != NULL) + { rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), root); + } else + { + Assert(rel->relid == root->parse->resultRelation); rte = planner_rt_fetch(rel->relid, root); + } Assert(rte->perminfoindex > 0); perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); if (rel->top_parent_relids != NULL) - updatedCols = translate_col_privs_recurse(root, rel->relid, - perminfo->updatedCols, - rel->top_parent_relids); + { + updatedCols = translate_col_privs_multilevel(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_multilevel(root, rel->relid, + root->extraUpdatedCols, + rel->top_parent_relids); + } else + { updatedCols = perminfo->updatedCols; - - /* extraUpdatedCols can be obtained directly from the RTE. */ - rte = planner_rt_fetch(rel->relid, root); - extraUpdatedCols = rte->extraUpdatedCols; + extraUpdatedCols = root->extraUpdatedCols; + } return bms_union(updatedCols, extraUpdatedCols); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 70f61ae7b1..f70fe2736c 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3643,6 +3643,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel, * 'resultRelations' is an integer list of actual RT indexes of target rel(s) * 'updateColnosLists' is a list of UPDATE target column number lists * (one sublist per rel); or NIL if not an UPDATE + * 'extraUpdatedColsBitmaps' is a list of generated column attribute number + * bitmapsets (one bitmapset per rel); or NIL if not an UPDATE * 'withCheckOptionLists' is a list of WCO lists (one per rel) * 'returningLists' is a list of RETURNING tlists (one per rel) * 'rowMarks' is a list of PlanRowMarks (non-locking only) @@ -3658,6 +3660,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -3722,6 +3725,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, pathnode->partColsUpdated = partColsUpdated; pathnode->resultRelations = resultRelations; pathnode->updateColnosLists = updateColnosLists; + pathnode->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; pathnode->withCheckOptionLists = withCheckOptionLists; pathnode->returningLists = returningLists; pathnode->rowMarks = rowMarks; diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index e834130151..b75455382e 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "optimizer/prep.h" #include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" @@ -1791,7 +1792,6 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; RTEPermissionInfo *target_perminfo; MemoryContext oldctx; @@ -1840,7 +1840,6 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { @@ -1858,7 +1857,8 @@ apply_handle_update(StringInfo s) } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); + edata->targetRelInfo->ri_extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 156c033bda..d62d457fc0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1592,46 +1592,6 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, } -/* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * columns that depend on any columns mentioned in - * target_perminfo->updatedCols. - */ -void -fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation) -{ - TupleDesc tupdesc = RelationGetDescr(target_relation); - TupleConstr *constr = tupdesc->constr; - - target_rte->extraUpdatedCols = NULL; - - if (constr && constr->has_generated_stored) - { - for (int i = 0; i < constr->num_defval; i++) - { - AttrDefault *defval = &constr->defval[i]; - Node *expr; - Bitmapset *attrs_used = NULL; - - /* skip if not generated column */ - if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) - continue; - - /* identify columns this generated column depends on */ - expr = stringToNode(defval->adbin); - pull_varattnos(expr, 1, &attrs_used); - - if (bms_overlap(target_perminfo->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, - defval->adnum - FirstLowInvalidHeapAttributeNumber); - } - } -} - - /* * matchLocks - * match the list of locks and returns the matching rules @@ -3670,7 +3630,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; - RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3682,7 +3641,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); - rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3767,9 +3725,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) parsetree->override, rt_entry_relation, NULL, 0, NULL); - - /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index c32834a9e8..a85570b1de 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -471,6 +471,9 @@ typedef struct ResultRelInfo /* Have the projection and the slots above been initialized? */ bool ri_projectNewInfoValid; + /* generated column attribute numbers */ + Bitmapset *ri_extraUpdatedCols; + /* triggers to be fired, if any */ TriggerDesc *ri_TrigDesc; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 080680ecd0..118a150d3c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1152,7 +1152,6 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ List *securityQuals; /* security barrier quals to apply, if any */ } RangeTblEntry; diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 99c8d4f611..04c7403897 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -422,6 +422,8 @@ struct PlannerInfo */ List *update_colnos; + Bitmapset *extraUpdatedCols; + /* * Fields filled during create_plan() for use in setrefs.c */ @@ -2250,6 +2252,7 @@ typedef struct ModifyTablePath bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *rowMarks; /* PlanRowMarks (non-locking only) */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index aaff24256e..2f8c9f65cc 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -237,6 +237,7 @@ typedef struct ModifyTable bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *fdwPrivLists; /* per-target-table FDW private data lists */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index 9a4f86920c..3cc54ef7e3 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -24,5 +24,8 @@ extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); +extern Bitmapset *translate_col_privs_multilevel(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 050f00e79a..fd16d94916 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -278,6 +278,7 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 5b4f350b33..92753c9670 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -16,6 +16,7 @@ #include "nodes/pathnodes.h" #include "nodes/plannodes.h" +#include "utils/relcache.h" /* @@ -39,6 +40,9 @@ extern void preprocess_targetlist(PlannerInfo *root); extern List *extract_update_targetlist_colnos(List *tlist); +extern Bitmapset *get_extraUpdatedCols(Bitmapset *updatedCols, + Relation target_relation); + extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex); /* diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 05c3680cd6..b4f96f298b 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,10 +24,6 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation); - extern Query *get_view_query(Relation view); extern const char *view_query_is_auto_updatable(Query *viewquery, bool check_cols); -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-07 01:04  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  2 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-10-07 01:04 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Thu, Oct 6, 2022 at 10:29 PM Amit Langote <amitlangote09@gmail.com> wrote:
> Actually, List of Bitmapsets turned out to be something that doesn't
> just-work with our Node infrastructure, which I found out thanks to
> -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> first-class support for copy/equal/write/read support for Bitmapsets,
> such that writeNode() can write appropriately labeled versions of them
> and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
> didn't actually go ahead and make *all* Bitmapsets in the plan trees
> to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
> to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
> just use *_NODE_FIELD().

All meson builds on the cfbot machines seem to have failed, maybe
because I didn't update src/include/nodes/meson.build to add
'nodes/bitmapset.h' to the `node_support_input_i` collection.  Here's
an updated version assuming that's the problem.  (Will set up meson
builds on my machine to avoid this in the future.)

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (4 files, .tar.gz)

v20-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 145.6K) download From 616768f11da3b92b72645bcd399aa8cb64e9c1da Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v20 1/4] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table looking for relations that need to have permissions checked. This can make the permission checking part of the executor initialization needlessly expensive when many inheritance children are present in the range range. While the permissions need not be checked on the individual child relations, the executor still must visit every range table entry to filter them out. This commit moves the permission checking information out of the range table entries into a new plan node called RTEPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RTEPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the correponding RTEPermissionInfo's index in the query's list of the latter. --- contrib/postgres_fdw/postgres_fdw.c | 82 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 12 +- contrib/sepgsql/sepgsql.h | 3 +- src/backend/access/common/attmap.c | 14 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 17 +- src/backend/commands/copyfrom.c | 11 +- src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 115 +++++------ src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 133 +++++++++---- src/backend/nodes/outfuncs.c | 6 +- src/backend/nodes/readfuncs.c | 6 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 43 +++- src/backend/optimizer/plan/subselect.c | 7 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 155 +++++++++++---- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 65 +++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 175 +++++++++-------- src/backend/parser/parse_target.c | 18 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 11 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 27 +-- src/backend/rewrite/rewriteHandler.c | 183 ++++++++---------- src/backend/rewrite/rewriteManip.c | 25 +++ src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 19 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 11 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 95 +++++---- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 9 +- src/include/parser/parse_relation.h | 4 + src/include/rewrite/rewriteHandler.h | 1 + src/include/rewrite/rewriteManip.h | 2 + .../modules/test_oat_hooks/test_oat_hooks.c | 12 +- 54 files changed, 951 insertions(+), 563 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index dd858aba03..9f233c37c4 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1512,16 +1514,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1813,7 +1814,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1892,6 +1894,36 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1903,6 +1935,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1910,6 +1943,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1933,6 +1967,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1944,7 +1979,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2147,6 +2183,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2224,6 +2261,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2240,7 +2279,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2626,7 +2666,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2646,13 +2685,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3955,12 +3993,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3972,12 +4010,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..c4e071b0ea 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -23,6 +23,7 @@ #include "commands/tablecmds.h" #include "executor/executor.h" #include "nodes/bitmapset.h" +#include "parser/parsetree.h" #include "sepgsql.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -277,38 +278,33 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, + bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, rtepermlist) { - RangeTblEntry *rte = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +335,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 87fdd972c2..129442b96e 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -35,7 +35,7 @@ PG_MODULE_MAGIC; * Saved hook entries (if stacked) */ static object_access_hook_type next_object_access_hook = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *rangeTbls, List *rtepermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (rangeTbls, rtepermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(rangeTbls, rtepermlist, abort)) return false; return true; @@ -471,8 +471,8 @@ _PG_init(void) object_access_hook = sepgsql_object_access; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = sepgsql_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = sepgsql_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f2a2c795bf..7aa6df92ec 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object, /* * dml.c */ -extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation); +extern bool sepgsql_dml_privileges(List *rangeTabls, List *rtepermlist, + bool abort_on_violation); /* * database.c diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..1e65d8a120 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,15 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error; AttrMap.attnums[] entry for such an + * outdesc column will be 0 in that case. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +240,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +262,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 49924e476a..5a62d5641d 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +155,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +177,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 175aa837f2..575ac5c81d 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the RTEPermissionInfos into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_rtepermlist = cstate->rtepermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1380,9 +1386,12 @@ BeginCopyFrom(ParseState *pstate, initStringInfo(&cstate->attribute_buf); - /* Assign range table, we'll need it in CopyFrom. */ + /* Assign range table and rtepermlist, we'll need them in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->rtepermlist = pstate->p_rtepermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index fd56066c13..622b574860 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1288,7 +1288,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1f774ac065..c2f50ae215 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1206,7 +1206,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9647,7 +9648,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9814,7 +9816,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10020,7 +10023,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10198,7 +10202,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12303,7 +12308,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18024,7 +18030,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18951,7 +18958,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index b5a0fc02e5..6f07ac2a9c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -411,10 +411,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d78862e660..c43d2215b3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,8 +74,8 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ -ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; +/* Hook for plugin to get control in ExecCheckPermissions() */ +ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook = NULL; /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +565,63 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rtepermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } - if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, - ereport_on_violation); + if (ExecutorCheckPermissions_hook) + result = (*ExecutorCheckPermissions_hook) (rangeTable, rtepermlist, + ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->rtepermlist, true); + estate->es_rtepermlist = plannedstmt->rtepermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 99512826c5..c1c5439fa1 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->rtepermlist = estate->es_rtepermlist; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 40e3c07693..b7b57ec404 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -780,7 +782,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -878,7 +881,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1140,7 +1144,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..461230b011 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent, something that's allowed with + * traditional inheritance, are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRTEPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RTEPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RTEPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,41 +1318,64 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 64c65f060b..b91e235423 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -504,6 +504,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -557,11 +558,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b4ff855f7c..75bf11c741 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -468,6 +468,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -531,11 +532,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ab4d8e201d..f854855951 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5794,7 +5797,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 5d0fd6e072..9576b69f1a 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrtepermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrtepermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -520,6 +523,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->rtepermlist = glob->finalrtepermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6210,6 +6214,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6337,6 +6342,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..a26aa36048 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -355,6 +355,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rtepermlist. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -370,14 +373,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ + rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) + { + /* + * Update perminfoindex, if any, to reflect the correponding + * RTEPermissionInfo's position in the flattened list. + */ + if (rte->perminfoindex > 0) + rte->perminfoindex += list_length(glob->finalrtepermlist); + add_rte_to_flat_rtable(glob, rte); + } + + rti++; } + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + root->parse->rtepermlist); + /* * If there are any dead subqueries, they are not referenced in the Plan * tree, so we must add RTEs contained in them to the flattened rtable @@ -450,6 +468,15 @@ flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) flatten_rtes_walker, (void *) glob, QTW_EXAMINE_RTES_BEFORE); + + /* + * Now add the subquery's RTEPermissionInfos too. flatten_rtes_walker() + * should already have updated the perminfoindex in the RTEs in the + * subquery to reflect the corresponding RTEPermissionInfos' position in + * finalrtepermlist. + */ + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + rte->subquery->rtepermlist); } static bool @@ -463,7 +490,15 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) + { + /* + * The correponding RTEPermissionInfo will get added to + * finalrtepermlist, so adjust perminfoindex accordingly. + */ + Assert(rte->perminfoindex > 0); + rte->perminfoindex += list_length(glob->finalrtepermlist); add_rte_to_flat_rtable(glob, rte); + } return false; } if (IsA(node, Query)) @@ -483,10 +518,10 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo for executor-startup + * permission checking. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..a61082d27c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,6 +1496,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, + subselect->rtable); + /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 41c7066d90..607f7ae907 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1205,6 +1198,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, + subquery->rtable); + /* * Now append the adjusted rtable entries to upper query. (We hold off * until after fixing the upper rtable entries; no point in running that @@ -1345,6 +1345,12 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, + subquery->rtepermlist, rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index cf7691a474..10c2aa13f6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *root_perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +312,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +332,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains the root partrel's + * updatedCols translated to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +409,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +468,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +491,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,33 +553,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ + /* Translate the bitmapset of generated columns being updated. */ if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); - } else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which @@ -866,3 +859,83 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * We need to get the updatedCols bitmapset from the relation's + * RTEPermissionInfo. + * + * If it's a simple "base" rel, can just fetch its RTEPermissionInfo that + * must always be present; this cannot get called on non-RELATION RTEs. + * + * For "other" rels, must look up the root parent relation mentioned in the + * query, because only that one gets assigned a RTEPermissionInfo, and + * translate the columns found therein to match the given relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + Assert(rte->perminfoindex > 0); + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + if (rel->top_parent_relids != NULL) + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + else + updatedCols = perminfo->updatedCols; + + /* extraUpdatedCols can be obtained directly from the RTE. */ + rte = planner_rt_fetch(rel->relid, root); + extraUpdatedCols = rte->extraUpdatedCols; + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index edcdd0a360..7711075ac2 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though + * only the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..25324d9486 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; + List *sub_rtepermlist; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -594,10 +596,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing - * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in - * that case we want the rule's OLD and NEW rtable entries to appear as - * part of the SELECT's rtable, not as outer references for it. (Kluge!) + * INSERT/SELECT, arrange to pass the rangetable/rtepermlist/namespace down + * to the SELECT. This can only happen if we are inside a CREATE RULE, + * and in that case we want the rule's OLD and NEW rtable entries to appear + * as part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ @@ -605,6 +607,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; + sub_rtepermlist = pstate->p_rtepermlist; + pstate->p_rtepermlist = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } @@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_rtepermlist = sub_rtepermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +899,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +915,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1102,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2432,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2444,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2485,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3252,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3341,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index c2b5474f5f..95cfe7d2b5 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3224,16 +3224,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RTEPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..5781a4b995 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 81f9ae2f02..4dc8d7ecf5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,10 +1021,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1238,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RTEPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1280,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1424,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1461,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1470,8 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1485,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1522,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1546,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1555,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1570,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1644,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1974,20 +1969,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1999,7 +1987,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2066,20 +2054,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2154,19 +2135,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2251,19 +2226,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2278,6 +2247,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2401,19 +2371,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2527,16 +2491,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2548,7 +2509,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3173,6 +3134,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3190,7 +3152,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3742,3 +3707,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRTEPermissionInfo + * Creates RTEPermissionInfo for a given RTE and adds it into the + * provided list + * + * Returns the RTEPermissionInfo and sets rte->perminfoindex. + */ +RTEPermissionInfo * +AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex == 0); + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RTEPermissionInfo); + perminfo->relid = rte->relid; + perminfo->inh = rte->inh; + /* Other information is set by fetching the node as and where needed. */ + + *rtepermlist = lappend(*rtepermlist, perminfo); + + /* Note its index. */ + rte->perminfoindex = list_length(*rtepermlist); + + return perminfo; +} + +/* + * GetRTEPermissionInfo + * Find RTEPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RTEPermissionInfo * +GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + rte->perminfoindex, rte->relid); + perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL); + if (perminfo->relid != rte->relid) + elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bd8057bc3e..0415fcec7e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1132,7 +1132,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1367,6 +1367,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1381,7 +1382,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1414,11 +1418,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * with its RTEPermissionInfo's selectedCols bitmap showing the whole row + * as needing select permission, as well as the individual columns. + * However, we can only get here for weird notations like (table.*).*, so + * it's not worth trying to clean up --- arguably, the permissions marking + * is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index bd068bba05..f542b43549 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1232,7 +1232,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3022,9 +3023,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3080,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->rtepermlist = pstate->p_rtepermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3122,8 +3121,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 207a5805ba..e834130151 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -492,6 +493,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRTEPermissionInfo(&estate->es_rtepermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1789,6 +1792,7 @@ apply_handle_update(StringInfo s) bool has_oldtup; TupleTableSlot *remoteslot; RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1837,6 +1841,7 @@ apply_handle_update(StringInfo s) * on the subscriber, since we are not touching those. */ target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1846,14 +1851,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2ecaa5b907..f2128190d8 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1125,7 +1125,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 213eabfbb9..5e9d226e54 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -785,14 +785,14 @@ 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 rtable entries. + * 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 will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * 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) @@ -819,18 +819,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RTEPermissionInfos for this query. */ + foreach(l, qry->rtepermlist) + { + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d02fd83c0a..7dbbbc629b 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -352,6 +352,7 @@ rewriteRuleAction(Query *parsetree, Query *sub_action; Query **sub_action_ptr; acquireLocksOnSubLinks_context context; + List *query_rtable; context.for_execute = true; @@ -394,32 +395,35 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. + * Also merge RTEPermissionInfo lists to ensure that all permissions are + * checked correctly. * * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. - * - * NOTE: because planner will destructively alter rtable, we must ensure - * that rule action's rtable is separate and shares no substructure with - * the main rtable. Hence do a deep copy here. + * NOTE: because planner will destructively alter rtable and rtepermlist, + * we must ensure that rule action's lists are separate and shares no + * substructure with the main query's lists. Hence do a deep copy here + * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), + * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtable = list_concat(copyObject(parsetree->rtable), - sub_action->rtable); + sub_action->rtepermlist = copyObject(sub_action->rtepermlist); + query_rtable = copyObject(parsetree->rtable); + ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, + parsetree->rtepermlist, query_rtable); + sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1590,10 +1594,13 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; @@ -1616,7 +1623,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RTEPermissionInfo *view_perminfo; + RTEPermissionInfo *base_perminfo; + RTEPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3175,6 +3151,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); Assert(base_rte->rtekind == RTE_RELATION); + base_perminfo = GetRTEPermissionInfo(viewquery->rtepermlist, base_rte); /* * Up to now, the base relation hasn't been touched at all in our query. @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RTEPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRTEPermissionInfo(&parsetree->rtepermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 101c39553a..3552a8db59 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1531,3 +1531,28 @@ ReplaceVarsFromTargetList(Node *node, (void *) &context, outer_hasSubLinks); } + +/* + * ConcatRTEPermissionInfoLists + * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * + * Also updates perminfoindex of the RTEs in src_rtable to point to the + * "source" perminfos after they have been added into *dest_rtepermlist. + */ +void +ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable) +{ + ListCell *l; + int offset = list_length(*dest_rtepermlist); + + *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + + foreach(l, src_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (rte->perminfoindex > 0) + rte->perminfoindex += offset; + } +} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b2a7237430..e4ce49d606 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RTEPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRTEPermissionInfo(root->rtepermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ab97e71dd7..baf8c542b8 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1598,6 +1599,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo; int clause_relid; Oid userid; @@ -1646,10 +1648,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); /* Table-level SELECT privilege is sufficient for all columns */ if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 1d503e7e01..1d4611fb94 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1376,6 +1376,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; + RTEPermissionInfo *pk_perminfo; + RTEPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1398,27 +1400,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; + + pk_perminfo = makeNode(RTEPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RTEPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fkrte, pkrte), + list_make2(fk_perminfo, pk_perminfo), false)) return false; /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 234fb66580..e1065db5c7 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5139,7 +5139,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5191,7 +5191,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5270,7 +5270,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5318,7 +5318,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5379,6 +5379,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5387,7 +5388,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5456,7 +5457,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 00dc0f2403..7c439a3cfb 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RTEPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index e37c6032ae..4fbb8801ff 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rtepermlist; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ed95ed1176..89b10ee909 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,9 +80,9 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); -extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPermissions_hook_type) (List *, List *, bool); +extern PGDLLIMPORT ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook; /* @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rtepermlist, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..c32834a9e8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rtepermlist; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 633e7671b3..080680ecd0 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -152,6 +152,9 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -965,37 +968,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1051,11 +1023,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1175,14 +1152,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 6bda383bea..99c8d4f611 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrtepermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 21e642a64c..aaff24256e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -72,6 +72,10 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -703,6 +707,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* user to perform the scan as */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..69665aba41 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rtepermlist: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rtepermlist; /* list of RTEPermissionInfo nodes for + * each RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rtepermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rtepermlist entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 484db165db..3cf475513b 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,9 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RTEPermissionInfo *AddRTEPermissionInfo(List **rtepermlist, + RangeTblEntry *rte); +extern RTEPermissionInfo *GetRTEPermissionInfo(List *rtepermlist, + RangeTblEntry *rte); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..05c3680cd6 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index f001ca41bb..0379dd9673 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,5 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); +extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable); #endif /* REWRITEMANIP_H */ diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c index 4b4e259cd2..bfa9263233 100644 --- a/src/test/modules/test_oat_hooks/test_oat_hooks.c +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -46,7 +46,7 @@ static bool REGRESS_suset_variable2 = false; /* Saved hook values */ static object_access_hook_type next_object_access_hook = NULL; static object_access_hook_type_str next_object_access_hook_str = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* Test Object Access Type Hook hooks */ @@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access, int subId, void *arg); static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg); -static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static bool REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort); static void REGRESS_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, @@ -219,8 +219,8 @@ _PG_init(void) object_access_hook_str = REGRESS_object_access_hook_str; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = REGRESS_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; @@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i } static bool -REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort) { bool am_super = superuser_arg(GetUserId()); bool allow = true; @@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) /* Forward to next hook in the chain */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + !(*next_exec_check_perms_hook) (rangeTabls, rtepermlist, do_abort)) allow = false; if (allow) -- 2.35.3
v20-0003-Allow-adding-Bitmapsets-as-Nodes-into-plan-trees.patch (application/octet-stream, 5.0K) download From 02fc2bb3c52f04b60c8da9d8682ba54c2a303137 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Thu, 6 Oct 2022 17:31:37 +0900 Subject: [PATCH v20 3/4] Allow adding Bitmapsets as Nodes into plan trees Note that this only adds some infrastructure bits and none of the existing bitmapsets that are added to plan trees have been changed to instead add the Node version. So, the plan trees, or really the bitmapsets contained in them, look the same as before as far as Node write/read functionality is concerned. This is needed, because it is not currently possible to write and then read back Bitmapsets that are not direct members of write/read capable Nodes; for example, if one needs to add a List of Bitmapsets to a plan tree. The most straightforward way to do that is to make Bitmapsets be written with outNode() and read with nodeRead(). --- src/backend/nodes/Makefile | 3 ++- src/backend/nodes/copyfuncs.c | 11 +++++++++++ src/backend/nodes/equalfuncs.c | 6 ++++++ src/backend/nodes/gen_node_support.pl | 1 + src/backend/nodes/outfuncs.c | 11 +++++++++++ src/backend/optimizer/prep/preptlist.c | 1 - src/include/nodes/bitmapset.h | 5 +++++ src/include/nodes/meson.build | 1 + 8 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile index 7450e191ee..da5307771b 100644 --- a/src/backend/nodes/Makefile +++ b/src/backend/nodes/Makefile @@ -57,7 +57,8 @@ node_headers = \ nodes/replnodes.h \ nodes/supportnodes.h \ nodes/value.h \ - utils/rel.h + utils/rel.h \ + nodes/bitmapset.h # see also catalog/Makefile for an explanation of these make rules diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e76fda8eba..1482019327 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -160,6 +160,17 @@ _copyExtensibleNode(const ExtensibleNode *from) return newnode; } +/* Custom copy routine for Node bitmapsets */ +static Bitmapset * +_copyBitmapset(const Bitmapset *from) +{ + Bitmapset *newnode = bms_copy(from); + + newnode->type = T_Bitmapset; + + return newnode; +} + /* * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 0373aa30fe..e8706c461a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -210,6 +210,12 @@ _equalList(const List *a, const List *b) return true; } +/* Custom equal routine for Node bitmapsets */ +static bool +_equalBitmapset(const Bitmapset *a, const Bitmapset *b) +{ + return bms_equal(a, b); +} /* * equal diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index 81b8c184a9..ccb5aff874 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -71,6 +71,7 @@ my @all_input_files = qw( nodes/supportnodes.h nodes/value.h utils/rel.h + nodes/bitmapset.h ); # Nodes from these input files are automatically treated as nodetag_only. diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b91e235423..d3beb907ea 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -328,6 +328,17 @@ outBitmapset(StringInfo str, const Bitmapset *bms) appendStringInfoChar(str, ')'); } +/* Custom write routine for Node bitmapsets */ +static void +_outBitmapset(StringInfo str, const Bitmapset *bms) +{ + Assert(IsA(bms, Bitmapset)); + WRITE_NODE_TYPE("BITMAPSET"); + + outBitmapset(str, bms); +} + + /* * Print the value of a Datum given its type. */ diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 137b28323d..e5c1103316 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -337,7 +337,6 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } - /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h index 75b5ce1a8e..9046ca177f 100644 --- a/src/include/nodes/bitmapset.h +++ b/src/include/nodes/bitmapset.h @@ -20,6 +20,8 @@ #ifndef BITMAPSET_H #define BITMAPSET_H +#include "nodes/nodes.h" + /* * Forward decl to save including pg_list.h */ @@ -48,6 +50,9 @@ typedef int32 signedbitmapword; /* must be the matching signed type */ typedef struct Bitmapset { + pg_node_attr(custom_copy_equal, custom_read_write) + + NodeTag type; int nwords; /* number of words in array */ bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */ } Bitmapset; diff --git a/src/include/nodes/meson.build b/src/include/nodes/meson.build index b7df232081..94701af8e1 100644 --- a/src/include/nodes/meson.build +++ b/src/include/nodes/meson.build @@ -19,6 +19,7 @@ node_support_input_i = [ 'nodes/supportnodes.h', 'nodes/value.h', 'utils/rel.h', + 'nodes/bitmapset.h', ] node_support_input = [] -- 2.35.3
v20-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 120.6K) download From e9925de0df0a5803c75cfc4b081d93404708d03d Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v20 2/4] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RTEPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add a copy of the original view RTE. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteDefine.c | 7 - src/backend/rewrite/rewriteHandler.c | 33 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 222 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 27 files changed, 729 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index cc9e39c4a5..b6c3749395 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2606,7 +2606,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2669,7 +2669,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6555,10 +6555,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6672,10 +6672,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b0747ce291..1d5f30443b 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 6f07ac2a9c..7e3d5e79bc 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,78 +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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -587,12 +515,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 5e9d226e54..ac2568e59a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -786,13 +786,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 7dbbbc629b..156c033bda 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before converting the RTE to become a SUBQUERY, store a copy for the + * executor to be able to lock the view relation and for the planner to be + * able to record the view relation OID in PlannedStmt.relationOids. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1858,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index a869321cdf..934a731205 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2225,7 +2225,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2241,7 +2241,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2257,7 +2257,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2273,7 +2273,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2291,7 +2291,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3283,7 +3283,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index fc2bd40be2..564a7ba1aa 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1623,7 +1623,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1675,7 +1675,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1691,7 +1691,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1707,7 +1707,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2192,15 +2192,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 346f594ad0..ecf4f65c12 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2479,8 +2479,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2491,8 +2491,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2518,8 +2518,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2531,8 +2531,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index bf4ff30d86..8f81a3098e 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1623,11 +1623,11 @@ returning pg_describe_object(classid, objid, objsubid) as obj, alter table tt14t drop column f3; -- column f3 is still in the view, sort of ... select pg_get_viewdef('tt14v', true); - pg_get_viewdef ---------------------------------- - SELECT t.f1, + - t."?dropped?column?" AS f3,+ - t.f4 + + pg_get_viewdef +------------------------------- + SELECT f1, + + "?dropped?column?" AS f3,+ + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1675,9 +1675,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1697,8 +1697,8 @@ create view tt14v as select t.f1, t.f4 from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1712,8 +1712,8 @@ alter table tt14t drop column f3; -- ok select pg_get_viewdef('tt14v', true); pg_get_viewdef ---------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1806,8 +1806,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -2054,7 +2054,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2106,19 +2106,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e2e62db6a2..fbb840e848 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 9dd137415e..19ef0a6b80 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1448,10 +1448,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1697,23 +1697,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1727,10 +1727,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1798,13 +1798,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1817,57 +1817,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1890,8 +1890,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1900,13 +1900,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2016,16 +2016,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2101,41 +2101,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2145,68 +2145,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2223,19 +2223,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2245,19 +2245,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2301,64 +2301,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2543,24 +2543,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3080,7 +3080,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3119,8 +3119,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3132,8 +3132,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3145,8 +3145,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3158,8 +3158,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 8b8eadd181..019c4726f6 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 55dcd668c9..ddaefdd91d 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1195,10 +1195,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1221,10 +1221,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1247,10 +1247,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1273,10 +1273,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1299,10 +1299,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1324,10 +1324,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1336,10 +1336,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7f2e32d8b0..f40197acbf 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 948b4e702c..cc213523c0 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 5fd3886b5e..3986fc1706 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
v20-0004-Add-per-result-relation-extraUpdatedCols-to-Modi.patch (application/octet-stream, 27.7K) download From 65c6d0e2808ad5d723036a3a9bb9cda72af51b13 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Tue, 4 Oct 2022 20:54:03 +0900 Subject: [PATCH v20 4/4] Add per-result-relation extraUpdatedCols to ModifyTable In spirit of removing things from RangeTblEntry that are better carried by Query or PlannedStmt directly. --- src/backend/executor/execUtils.c | 9 ++-- src/backend/executor/nodeModifyTable.c | 4 ++ src/backend/nodes/outfuncs.c | 1 - src/backend/nodes/readfuncs.c | 1 - src/backend/optimizer/plan/createplan.c | 6 ++- src/backend/optimizer/plan/planner.c | 17 +++++++ src/backend/optimizer/prep/preptlist.c | 49 ++++++++++++++++++ src/backend/optimizer/util/inherit.c | 64 +++++++++++++++--------- src/backend/optimizer/util/pathnode.c | 4 ++ src/backend/replication/logical/worker.c | 6 +-- src/backend/rewrite/rewriteHandler.c | 45 ----------------- src/include/nodes/execnodes.h | 3 ++ src/include/nodes/parsenodes.h | 1 - src/include/nodes/pathnodes.h | 3 ++ src/include/nodes/plannodes.h | 1 + src/include/optimizer/inherit.h | 3 ++ src/include/optimizer/pathnode.h | 1 + src/include/optimizer/prep.h | 4 ++ src/include/rewrite/rewriteHandler.h | 4 -- 19 files changed, 138 insertions(+), 88 deletions(-) diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 461230b011..ede4c98875 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1378,20 +1378,17 @@ ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { if (relinfo->ri_RangeTableIndex != 0) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + return relinfo->ri_extraUpdatedCols; } else if (relinfo->ri_RootResultRelInfo) { ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); if (relinfo->ri_RootToPartitionMap != NULL) return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); + rootRelInfo->ri_extraUpdatedCols); else - return rte->extraUpdatedCols; + return rootRelInfo->ri_extraUpdatedCols; } else return NULL; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 04454ad6e6..e034e3ceee 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3971,6 +3971,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) { resultRelInfo = &mtstate->resultRelInfo[i]; + if (node->extraUpdatedColsBitmaps) + resultRelInfo->ri_extraUpdatedCols = + list_nth(node->extraUpdatedColsBitmaps, i); + /* Let FDWs init themselves for foreign-table result rels */ if (!resultRelInfo->ri_usesFdwDirectModify && resultRelInfo->ri_FdwRoutine != NULL && diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index d3beb907ea..139d8e095f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -569,7 +569,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 75bf11c741..bcd380516d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -532,7 +532,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f854855951..5c1c7aed2f 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -308,7 +308,7 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); @@ -2824,6 +2824,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->partColsUpdated, best_path->resultRelations, best_path->updateColnosLists, + best_path->extraUpdatedColsBitmaps, best_path->withCheckOptionLists, best_path->returningLists, best_path->rowMarks, @@ -6980,7 +6981,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -7049,6 +7050,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, node->exclRelTlist = onconflict->exclRelTlist; } node->updateColnosLists = updateColnosLists; + node->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; node->withCheckOptionLists = withCheckOptionLists; node->returningLists = returningLists; node->rowMarks = rowMarks; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9576b69f1a..8913200879 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1746,6 +1746,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) Index rootRelation; List *resultRelations = NIL; List *updateColnosLists = NIL; + List *extraUpdatedColsBitmaps = NIL; List *withCheckOptionLists = NIL; List *returningLists = NIL; List *mergeActionLists = NIL; @@ -1779,15 +1780,24 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) if (parse->commandType == CMD_UPDATE) { List *update_colnos = root->update_colnos; + Bitmapset *extraUpdatedCols = root->extraUpdatedCols; if (this_result_rel != top_result_rel) + { update_colnos = adjust_inherited_attnums_multilevel(root, update_colnos, this_result_rel->relid, top_result_rel->relid); + extraUpdatedCols = + translate_col_privs_multilevel(root, resultRelation, + extraUpdatedCols, + this_result_rel->top_parent_relids); + } updateColnosLists = lappend(updateColnosLists, update_colnos); + extraUpdatedColsBitmaps = lappend(extraUpdatedColsBitmaps, + extraUpdatedCols); } if (parse->withCheckOptions) { @@ -1869,7 +1879,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1883,7 +1896,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) /* Single-relation INSERT/UPDATE/DELETE. */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1922,6 +1938,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) root->partColsUpdated, resultRelations, updateColnosLists, + extraUpdatedColsBitmaps, withCheckOptionLists, returningLists, rowMarks, diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index e5c1103316..10a948ed40 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -43,6 +43,7 @@ #include "optimizer/prep.h" #include "optimizer/tlist.h" #include "parser/parse_coerce.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "utils/rel.h" @@ -106,6 +107,17 @@ preprocess_targetlist(PlannerInfo *root) else if (command_type == CMD_UPDATE) root->update_colnos = extract_update_targetlist_colnos(tlist); + /* Also populate extraUpdatedCols (for generated columns) */ + if (command_type == CMD_UPDATE) + { + RTEPermissionInfo *target_perminfo = + GetRTEPermissionInfo(parse->rtepermlist, target_rte); + + root->extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, + target_relation); + } + /* * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s) * needed to allow the executor to identify the rows to be updated or @@ -337,6 +349,43 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } +/* + * Return the indexes of any generated columns that depend on any columns + * mentioned in target_perminfo->updatedCols. + */ +Bitmapset * +get_extraUpdatedCols(Bitmapset *updatedCols, Relation target_relation) +{ + TupleDesc tupdesc = RelationGetDescr(target_relation); + TupleConstr *constr = tupdesc->constr; + /* These go into ModifyTable.extraUpdatedColsBitmaps, so make as Nodes. */ + Bitmapset *extraUpdatedCols = makeNode(Bitmapset); + + if (constr && constr->has_generated_stored) + { + for (int i = 0; i < constr->num_defval; i++) + { + AttrDefault *defval = &constr->defval[i]; + Node *expr; + Bitmapset *attrs_used = NULL; + + /* skip if not generated column */ + if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) + continue; + + /* identify columns this generated column depends on */ + expr = stringToNode(defval->adbin); + pull_varattnos(expr, 1, &attrs_used); + + if (bms_overlap(updatedCols, attrs_used)) + extraUpdatedCols = bms_add_member(extraUpdatedCols, + defval->adnum - FirstLowInvalidHeapAttributeNumber); + } + } + + return extraUpdatedCols; +} + /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 10c2aa13f6..ea632a94a6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -40,6 +40,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -148,6 +149,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, expand_partitioned_rtentry(root, rel, rte, rti, oldrelation, root_perminfo->updatedCols, + root->extraUpdatedCols, oldrc, lockmode); } else @@ -313,6 +315,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -343,7 +346,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -412,14 +415,18 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, { AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; child_updatedCols = translate_col_privs(parent_updatedCols, appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, childrel, child_updatedCols, + child_extraUpdatedCols, top_parentrc, lockmode); } @@ -456,7 +463,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -486,7 +492,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else @@ -553,13 +559,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* Translate the bitmapset of generated columns being updated. */ - if (childOID != parentOID) - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - else - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -656,7 +655,8 @@ static Bitmapset * translate_col_privs(const Bitmapset *parent_privs, List *translated_vars) { - Bitmapset *child_privs = NULL; + /* These go into ModifyTable.extraUpdatedColsBitmaps, so make as Nodes. */ + Bitmapset *child_privs = makeNode(Bitmapset); bool whole_row; int attno; ListCell *lc; @@ -866,13 +866,16 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, * 'top_parent_cols' to the columns numbers of a descendent relation * given by 'relid' */ -static Bitmapset * -translate_col_privs_recurse(PlannerInfo *root, Index relid, - Bitmapset *top_parent_cols, - Relids top_parent_relids) +Bitmapset * +translate_col_privs_multilevel(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) { AppendRelInfo *appinfo; + if (top_parent_cols == NULL) + return NULL; + Assert(root->append_rel_array != NULL); appinfo = root->append_rel_array[relid]; Assert(appinfo != NULL); @@ -883,9 +886,9 @@ translate_col_privs_recurse(PlannerInfo *root, Index relid, * and child relation pairs. */ if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) - translate_col_privs_recurse(root, appinfo->parent_relid, - top_parent_cols, - top_parent_relids); + translate_col_privs_multilevel(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); return translate_col_privs(top_parent_cols, appinfo->translated_vars); } @@ -903,6 +906,8 @@ GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) Bitmapset *updatedCols, *extraUpdatedCols; + Assert(root->parse->commandType == CMD_UPDATE); + if (!IS_SIMPLE_REL(rel)) return NULL; @@ -918,24 +923,33 @@ GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) * translate the columns found therein to match the given relation. */ if (rel->top_parent_relids != NULL) + { rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), root); + } else + { + Assert(rel->relid == root->parse->resultRelation); rte = planner_rt_fetch(rel->relid, root); + } Assert(rte->perminfoindex > 0); perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); if (rel->top_parent_relids != NULL) - updatedCols = translate_col_privs_recurse(root, rel->relid, - perminfo->updatedCols, - rel->top_parent_relids); + { + updatedCols = translate_col_privs_multilevel(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_multilevel(root, rel->relid, + root->extraUpdatedCols, + rel->top_parent_relids); + } else + { updatedCols = perminfo->updatedCols; - - /* extraUpdatedCols can be obtained directly from the RTE. */ - rte = planner_rt_fetch(rel->relid, root); - extraUpdatedCols = rte->extraUpdatedCols; + extraUpdatedCols = root->extraUpdatedCols; + } return bms_union(updatedCols, extraUpdatedCols); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 70f61ae7b1..f70fe2736c 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3643,6 +3643,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel, * 'resultRelations' is an integer list of actual RT indexes of target rel(s) * 'updateColnosLists' is a list of UPDATE target column number lists * (one sublist per rel); or NIL if not an UPDATE + * 'extraUpdatedColsBitmaps' is a list of generated column attribute number + * bitmapsets (one bitmapset per rel); or NIL if not an UPDATE * 'withCheckOptionLists' is a list of WCO lists (one per rel) * 'returningLists' is a list of RETURNING tlists (one per rel) * 'rowMarks' is a list of PlanRowMarks (non-locking only) @@ -3658,6 +3660,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -3722,6 +3725,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, pathnode->partColsUpdated = partColsUpdated; pathnode->resultRelations = resultRelations; pathnode->updateColnosLists = updateColnosLists; + pathnode->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; pathnode->withCheckOptionLists = withCheckOptionLists; pathnode->returningLists = returningLists; pathnode->rowMarks = rowMarks; diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index e834130151..b75455382e 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "optimizer/prep.h" #include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" @@ -1791,7 +1792,6 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; RTEPermissionInfo *target_perminfo; MemoryContext oldctx; @@ -1840,7 +1840,6 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { @@ -1858,7 +1857,8 @@ apply_handle_update(StringInfo s) } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); + edata->targetRelInfo->ri_extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 156c033bda..d62d457fc0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1592,46 +1592,6 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, } -/* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * columns that depend on any columns mentioned in - * target_perminfo->updatedCols. - */ -void -fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation) -{ - TupleDesc tupdesc = RelationGetDescr(target_relation); - TupleConstr *constr = tupdesc->constr; - - target_rte->extraUpdatedCols = NULL; - - if (constr && constr->has_generated_stored) - { - for (int i = 0; i < constr->num_defval; i++) - { - AttrDefault *defval = &constr->defval[i]; - Node *expr; - Bitmapset *attrs_used = NULL; - - /* skip if not generated column */ - if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) - continue; - - /* identify columns this generated column depends on */ - expr = stringToNode(defval->adbin); - pull_varattnos(expr, 1, &attrs_used); - - if (bms_overlap(target_perminfo->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, - defval->adnum - FirstLowInvalidHeapAttributeNumber); - } - } -} - - /* * matchLocks - * match the list of locks and returns the matching rules @@ -3670,7 +3630,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; - RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3682,7 +3641,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); - rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3767,9 +3725,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) parsetree->override, rt_entry_relation, NULL, 0, NULL); - - /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index c32834a9e8..a85570b1de 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -471,6 +471,9 @@ typedef struct ResultRelInfo /* Have the projection and the slots above been initialized? */ bool ri_projectNewInfoValid; + /* generated column attribute numbers */ + Bitmapset *ri_extraUpdatedCols; + /* triggers to be fired, if any */ TriggerDesc *ri_TrigDesc; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 080680ecd0..118a150d3c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1152,7 +1152,6 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ List *securityQuals; /* security barrier quals to apply, if any */ } RangeTblEntry; diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 99c8d4f611..04c7403897 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -422,6 +422,8 @@ struct PlannerInfo */ List *update_colnos; + Bitmapset *extraUpdatedCols; + /* * Fields filled during create_plan() for use in setrefs.c */ @@ -2250,6 +2252,7 @@ typedef struct ModifyTablePath bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *rowMarks; /* PlanRowMarks (non-locking only) */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index aaff24256e..2f8c9f65cc 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -237,6 +237,7 @@ typedef struct ModifyTable bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *fdwPrivLists; /* per-target-table FDW private data lists */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index 9a4f86920c..3cc54ef7e3 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -24,5 +24,8 @@ extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); +extern Bitmapset *translate_col_privs_multilevel(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 050f00e79a..fd16d94916 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -278,6 +278,7 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 5b4f350b33..92753c9670 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -16,6 +16,7 @@ #include "nodes/pathnodes.h" #include "nodes/plannodes.h" +#include "utils/relcache.h" /* @@ -39,6 +40,9 @@ extern void preprocess_targetlist(PlannerInfo *root); extern List *extract_update_targetlist_colnos(List *tlist); +extern Bitmapset *get_extraUpdatedCols(Bitmapset *updatedCols, + Relation target_relation); + extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex); /* diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 05c3680cd6..b4f96f298b 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,10 +24,6 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation); - extern Query *get_view_query(Relation view); extern const char *view_query_is_auto_updatable(Query *viewquery, bool check_cols); -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-07 04:25  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-10-07 04:25 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Oct 7, 2022 at 10:04 AM Amit Langote <amitlangote09@gmail.com> wrote:
> On Thu, Oct 6, 2022 at 10:29 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > Actually, List of Bitmapsets turned out to be something that doesn't
> > just-work with our Node infrastructure, which I found out thanks to
> > -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> > first-class support for copy/equal/write/read support for Bitmapsets,
> > such that writeNode() can write appropriately labeled versions of them
> > and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
> > didn't actually go ahead and make *all* Bitmapsets in the plan trees
> > to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
> > to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
> > just use *_NODE_FIELD().
>
> All meson builds on the cfbot machines seem to have failed, maybe
> because I didn't update src/include/nodes/meson.build to add
> 'nodes/bitmapset.h' to the `node_support_input_i` collection.  Here's
> an updated version assuming that's the problem.  (Will set up meson
> builds on my machine to avoid this in the future.)

And... noticed that a postgres_fdw test failed, because
_readBitmapset() not having been changed to set NodeTag would
"corrupt" any Bitmapsets that were created with it set.

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (4 files, .tar.gz)

v21-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 145.6K) download From fe5ee4bc42b1d5e3ee506e8b5503650d7c376fc2 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v21 1/4] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table looking for relations that need to have permissions checked. This can make the permission checking part of the executor initialization needlessly expensive when many inheritance children are present in the range range. While the permissions need not be checked on the individual child relations, the executor still must visit every range table entry to filter them out. This commit moves the permission checking information out of the range table entries into a new plan node called RTEPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RTEPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the correponding RTEPermissionInfo's index in the query's list of the latter. --- contrib/postgres_fdw/postgres_fdw.c | 82 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 12 +- contrib/sepgsql/sepgsql.h | 3 +- src/backend/access/common/attmap.c | 14 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 17 +- src/backend/commands/copyfrom.c | 11 +- src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 115 +++++------ src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 133 +++++++++---- src/backend/nodes/outfuncs.c | 6 +- src/backend/nodes/readfuncs.c | 6 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 43 +++- src/backend/optimizer/plan/subselect.c | 7 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 155 +++++++++++---- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 65 +++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 175 +++++++++-------- src/backend/parser/parse_target.c | 18 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 11 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 27 +-- src/backend/rewrite/rewriteHandler.c | 183 ++++++++---------- src/backend/rewrite/rewriteManip.c | 25 +++ src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 19 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 11 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 95 +++++---- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 9 +- src/include/parser/parse_relation.h | 4 + src/include/rewrite/rewriteHandler.h | 1 + src/include/rewrite/rewriteManip.h | 2 + .../modules/test_oat_hooks/test_oat_hooks.c | 12 +- 54 files changed, 951 insertions(+), 563 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 8d013f5b1a..de913fc4ae 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1510,16 +1512,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1811,7 +1812,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1890,6 +1892,36 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1901,6 +1933,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1908,6 +1941,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1931,6 +1965,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1942,7 +1977,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2145,6 +2181,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2222,6 +2259,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2238,7 +2277,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2624,7 +2664,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2644,13 +2683,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3953,12 +3991,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3970,12 +4008,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..c4e071b0ea 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -23,6 +23,7 @@ #include "commands/tablecmds.h" #include "executor/executor.h" #include "nodes/bitmapset.h" +#include "parser/parsetree.h" #include "sepgsql.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -277,38 +278,33 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, + bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, rtepermlist) { - RangeTblEntry *rte = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +335,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 87fdd972c2..129442b96e 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -35,7 +35,7 @@ PG_MODULE_MAGIC; * Saved hook entries (if stacked) */ static object_access_hook_type next_object_access_hook = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *rangeTbls, List *rtepermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (rangeTbls, rtepermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(rangeTbls, rtepermlist, abort)) return false; return true; @@ -471,8 +471,8 @@ _PG_init(void) object_access_hook = sepgsql_object_access; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = sepgsql_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = sepgsql_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f2a2c795bf..7aa6df92ec 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object, /* * dml.c */ -extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation); +extern bool sepgsql_dml_privileges(List *rangeTabls, List *rtepermlist, + bool abort_on_violation); /* * database.c diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..1e65d8a120 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,15 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error; AttrMap.attnums[] entry for such an + * outdesc column will be 0 in that case. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +240,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +262,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 49924e476a..5a62d5641d 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +155,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +177,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 175aa837f2..575ac5c81d 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the RTEPermissionInfos into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_rtepermlist = cstate->rtepermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1380,9 +1386,12 @@ BeginCopyFrom(ParseState *pstate, initStringInfo(&cstate->attribute_buf); - /* Assign range table, we'll need it in CopyFrom. */ + /* Assign range table and rtepermlist, we'll need them in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->rtepermlist = pstate->p_rtepermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index fd56066c13..622b574860 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1288,7 +1288,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1f774ac065..c2f50ae215 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1206,7 +1206,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9647,7 +9648,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9814,7 +9816,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10020,7 +10023,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10198,7 +10202,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12303,7 +12308,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18024,7 +18030,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18951,7 +18958,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index b5a0fc02e5..6f07ac2a9c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -411,10 +411,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d78862e660..c43d2215b3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,8 +74,8 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ -ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; +/* Hook for plugin to get control in ExecCheckPermissions() */ +ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook = NULL; /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +565,63 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rtepermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } - if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, - ereport_on_violation); + if (ExecutorCheckPermissions_hook) + result = (*ExecutorCheckPermissions_hook) (rangeTable, rtepermlist, + ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->rtepermlist, true); + estate->es_rtepermlist = plannedstmt->rtepermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 99512826c5..c1c5439fa1 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->rtepermlist = estate->es_rtepermlist; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 40e3c07693..b7b57ec404 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -780,7 +782,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -878,7 +881,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1140,7 +1144,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..461230b011 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent, something that's allowed with + * traditional inheritance, are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRTEPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RTEPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RTEPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,41 +1318,64 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 64c65f060b..b91e235423 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -504,6 +504,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -557,11 +558,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b4ff855f7c..75bf11c741 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -468,6 +468,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -531,11 +532,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ab4d8e201d..f854855951 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5794,7 +5797,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 5d0fd6e072..9576b69f1a 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrtepermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrtepermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -520,6 +523,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->rtepermlist = glob->finalrtepermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6210,6 +6214,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6337,6 +6342,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..a26aa36048 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -355,6 +355,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rtepermlist. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -370,14 +373,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ + rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) + { + /* + * Update perminfoindex, if any, to reflect the correponding + * RTEPermissionInfo's position in the flattened list. + */ + if (rte->perminfoindex > 0) + rte->perminfoindex += list_length(glob->finalrtepermlist); + add_rte_to_flat_rtable(glob, rte); + } + + rti++; } + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + root->parse->rtepermlist); + /* * If there are any dead subqueries, they are not referenced in the Plan * tree, so we must add RTEs contained in them to the flattened rtable @@ -450,6 +468,15 @@ flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) flatten_rtes_walker, (void *) glob, QTW_EXAMINE_RTES_BEFORE); + + /* + * Now add the subquery's RTEPermissionInfos too. flatten_rtes_walker() + * should already have updated the perminfoindex in the RTEs in the + * subquery to reflect the corresponding RTEPermissionInfos' position in + * finalrtepermlist. + */ + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + rte->subquery->rtepermlist); } static bool @@ -463,7 +490,15 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) + { + /* + * The correponding RTEPermissionInfo will get added to + * finalrtepermlist, so adjust perminfoindex accordingly. + */ + Assert(rte->perminfoindex > 0); + rte->perminfoindex += list_length(glob->finalrtepermlist); add_rte_to_flat_rtable(glob, rte); + } return false; } if (IsA(node, Query)) @@ -483,10 +518,10 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo for executor-startup + * permission checking. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..a61082d27c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,6 +1496,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, + subselect->rtable); + /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 41c7066d90..607f7ae907 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1205,6 +1198,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, + subquery->rtable); + /* * Now append the adjusted rtable entries to upper query. (We hold off * until after fixing the upper rtable entries; no point in running that @@ -1345,6 +1345,12 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, + subquery->rtepermlist, rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index cf7691a474..10c2aa13f6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *root_perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +312,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +332,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains the root partrel's + * updatedCols translated to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +409,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +468,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +491,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,33 +553,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ + /* Translate the bitmapset of generated columns being updated. */ if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); - } else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which @@ -866,3 +859,83 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * We need to get the updatedCols bitmapset from the relation's + * RTEPermissionInfo. + * + * If it's a simple "base" rel, can just fetch its RTEPermissionInfo that + * must always be present; this cannot get called on non-RELATION RTEs. + * + * For "other" rels, must look up the root parent relation mentioned in the + * query, because only that one gets assigned a RTEPermissionInfo, and + * translate the columns found therein to match the given relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + Assert(rte->perminfoindex > 0); + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + if (rel->top_parent_relids != NULL) + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + else + updatedCols = perminfo->updatedCols; + + /* extraUpdatedCols can be obtained directly from the RTE. */ + rte = planner_rt_fetch(rel->relid, root); + extraUpdatedCols = rte->extraUpdatedCols; + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index edcdd0a360..7711075ac2 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though + * only the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..25324d9486 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; + List *sub_rtepermlist; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -594,10 +596,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing - * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in - * that case we want the rule's OLD and NEW rtable entries to appear as - * part of the SELECT's rtable, not as outer references for it. (Kluge!) + * INSERT/SELECT, arrange to pass the rangetable/rtepermlist/namespace down + * to the SELECT. This can only happen if we are inside a CREATE RULE, + * and in that case we want the rule's OLD and NEW rtable entries to appear + * as part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ @@ -605,6 +607,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; + sub_rtepermlist = pstate->p_rtepermlist; + pstate->p_rtepermlist = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } @@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_rtepermlist = sub_rtepermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +899,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +915,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1102,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2432,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2444,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2485,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3252,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3341,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index c2b5474f5f..95cfe7d2b5 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3224,16 +3224,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RTEPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..5781a4b995 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 81f9ae2f02..4dc8d7ecf5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,10 +1021,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1238,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RTEPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1280,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1424,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1461,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1470,8 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1485,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1522,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1546,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1555,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1570,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1644,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1974,20 +1969,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1999,7 +1987,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2066,20 +2054,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2154,19 +2135,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2251,19 +2226,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2278,6 +2247,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2401,19 +2371,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2527,16 +2491,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2548,7 +2509,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3173,6 +3134,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3190,7 +3152,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3742,3 +3707,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRTEPermissionInfo + * Creates RTEPermissionInfo for a given RTE and adds it into the + * provided list + * + * Returns the RTEPermissionInfo and sets rte->perminfoindex. + */ +RTEPermissionInfo * +AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex == 0); + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RTEPermissionInfo); + perminfo->relid = rte->relid; + perminfo->inh = rte->inh; + /* Other information is set by fetching the node as and where needed. */ + + *rtepermlist = lappend(*rtepermlist, perminfo); + + /* Note its index. */ + rte->perminfoindex = list_length(*rtepermlist); + + return perminfo; +} + +/* + * GetRTEPermissionInfo + * Find RTEPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RTEPermissionInfo * +GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + rte->perminfoindex, rte->relid); + perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL); + if (perminfo->relid != rte->relid) + elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bd8057bc3e..0415fcec7e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1132,7 +1132,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1367,6 +1367,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1381,7 +1382,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1414,11 +1418,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * with its RTEPermissionInfo's selectedCols bitmap showing the whole row + * as needing select permission, as well as the individual columns. + * However, we can only get here for weird notations like (table.*).*, so + * it's not worth trying to clean up --- arguably, the permissions marking + * is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index bd068bba05..f542b43549 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1232,7 +1232,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3022,9 +3023,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3080,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->rtepermlist = pstate->p_rtepermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3122,8 +3121,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 207a5805ba..e834130151 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -492,6 +493,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRTEPermissionInfo(&estate->es_rtepermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1789,6 +1792,7 @@ apply_handle_update(StringInfo s) bool has_oldtup; TupleTableSlot *remoteslot; RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1837,6 +1841,7 @@ apply_handle_update(StringInfo s) * on the subscriber, since we are not touching those. */ target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1846,14 +1851,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2ecaa5b907..f2128190d8 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1125,7 +1125,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 213eabfbb9..5e9d226e54 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -785,14 +785,14 @@ 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 rtable entries. + * 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 will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * 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) @@ -819,18 +819,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RTEPermissionInfos for this query. */ + foreach(l, qry->rtepermlist) + { + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d02fd83c0a..7dbbbc629b 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -352,6 +352,7 @@ rewriteRuleAction(Query *parsetree, Query *sub_action; Query **sub_action_ptr; acquireLocksOnSubLinks_context context; + List *query_rtable; context.for_execute = true; @@ -394,32 +395,35 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. + * Also merge RTEPermissionInfo lists to ensure that all permissions are + * checked correctly. * * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. - * - * NOTE: because planner will destructively alter rtable, we must ensure - * that rule action's rtable is separate and shares no substructure with - * the main rtable. Hence do a deep copy here. + * NOTE: because planner will destructively alter rtable and rtepermlist, + * we must ensure that rule action's lists are separate and shares no + * substructure with the main query's lists. Hence do a deep copy here + * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), + * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtable = list_concat(copyObject(parsetree->rtable), - sub_action->rtable); + sub_action->rtepermlist = copyObject(sub_action->rtepermlist); + query_rtable = copyObject(parsetree->rtable); + ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, + parsetree->rtepermlist, query_rtable); + sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1590,10 +1594,13 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; @@ -1616,7 +1623,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RTEPermissionInfo *view_perminfo; + RTEPermissionInfo *base_perminfo; + RTEPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3175,6 +3151,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); Assert(base_rte->rtekind == RTE_RELATION); + base_perminfo = GetRTEPermissionInfo(viewquery->rtepermlist, base_rte); /* * Up to now, the base relation hasn't been touched at all in our query. @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RTEPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRTEPermissionInfo(&parsetree->rtepermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 101c39553a..3552a8db59 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1531,3 +1531,28 @@ ReplaceVarsFromTargetList(Node *node, (void *) &context, outer_hasSubLinks); } + +/* + * ConcatRTEPermissionInfoLists + * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * + * Also updates perminfoindex of the RTEs in src_rtable to point to the + * "source" perminfos after they have been added into *dest_rtepermlist. + */ +void +ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable) +{ + ListCell *l; + int offset = list_length(*dest_rtepermlist); + + *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + + foreach(l, src_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (rte->perminfoindex > 0) + rte->perminfoindex += offset; + } +} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b2a7237430..e4ce49d606 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RTEPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRTEPermissionInfo(root->rtepermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ab97e71dd7..baf8c542b8 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1598,6 +1599,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo; int clause_relid; Oid userid; @@ -1646,10 +1648,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); /* Table-level SELECT privilege is sufficient for all columns */ if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 1d503e7e01..1d4611fb94 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1376,6 +1376,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; + RTEPermissionInfo *pk_perminfo; + RTEPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1398,27 +1400,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; + + pk_perminfo = makeNode(RTEPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RTEPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fkrte, pkrte), + list_make2(fk_perminfo, pk_perminfo), false)) return false; /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 234fb66580..e1065db5c7 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5139,7 +5139,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5191,7 +5191,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5270,7 +5270,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5318,7 +5318,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5379,6 +5379,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5387,7 +5388,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5456,7 +5457,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 00dc0f2403..7c439a3cfb 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RTEPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index e37c6032ae..4fbb8801ff 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rtepermlist; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ed95ed1176..89b10ee909 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,9 +80,9 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); -extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPermissions_hook_type) (List *, List *, bool); +extern PGDLLIMPORT ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook; /* @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rtepermlist, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..c32834a9e8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rtepermlist; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 633e7671b3..080680ecd0 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -152,6 +152,9 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -965,37 +968,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1051,11 +1023,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1175,14 +1152,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 6bda383bea..99c8d4f611 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrtepermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 21e642a64c..aaff24256e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -72,6 +72,10 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -703,6 +707,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* user to perform the scan as */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..69665aba41 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rtepermlist: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rtepermlist; /* list of RTEPermissionInfo nodes for + * each RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rtepermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rtepermlist entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 484db165db..3cf475513b 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,9 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RTEPermissionInfo *AddRTEPermissionInfo(List **rtepermlist, + RangeTblEntry *rte); +extern RTEPermissionInfo *GetRTEPermissionInfo(List *rtepermlist, + RangeTblEntry *rte); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..05c3680cd6 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index f001ca41bb..0379dd9673 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,5 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); +extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable); #endif /* REWRITEMANIP_H */ diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c index 4b4e259cd2..bfa9263233 100644 --- a/src/test/modules/test_oat_hooks/test_oat_hooks.c +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -46,7 +46,7 @@ static bool REGRESS_suset_variable2 = false; /* Saved hook values */ static object_access_hook_type next_object_access_hook = NULL; static object_access_hook_type_str next_object_access_hook_str = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* Test Object Access Type Hook hooks */ @@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access, int subId, void *arg); static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg); -static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static bool REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort); static void REGRESS_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, @@ -219,8 +219,8 @@ _PG_init(void) object_access_hook_str = REGRESS_object_access_hook_str; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = REGRESS_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; @@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i } static bool -REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort) { bool am_super = superuser_arg(GetUserId()); bool allow = true; @@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) /* Forward to next hook in the chain */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + !(*next_exec_check_perms_hook) (rangeTabls, rtepermlist, do_abort)) allow = false; if (allow) -- 2.35.3
v21-0003-Allow-adding-Bitmapsets-as-Nodes-into-plan-trees.patch (application/octet-stream, 5.5K) download From be35773af5f29b9406d052238b79dc0a08dcef83 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Thu, 6 Oct 2022 17:31:37 +0900 Subject: [PATCH v21 3/4] Allow adding Bitmapsets as Nodes into plan trees Note that this only adds some infrastructure bits and none of the existing bitmapsets that are added to plan trees have been changed to instead add the Node version. So, the plan trees, or really the bitmapsets contained in them, look the same as before as far as Node write/read functionality is concerned. This is needed, because it is not currently possible to write and then read back Bitmapsets that are not direct members of write/read capable Nodes; for example, if one needs to add a List of Bitmapsets to a plan tree. The most straightforward way to do that is to make Bitmapsets be written with outNode() and read with nodeRead(). --- src/backend/nodes/Makefile | 3 ++- src/backend/nodes/copyfuncs.c | 11 +++++++++++ src/backend/nodes/equalfuncs.c | 6 ++++++ src/backend/nodes/gen_node_support.pl | 1 + src/backend/nodes/outfuncs.c | 11 +++++++++++ src/backend/nodes/readfuncs.c | 4 ++++ src/backend/optimizer/prep/preptlist.c | 1 - src/include/nodes/bitmapset.h | 5 +++++ src/include/nodes/meson.build | 1 + 9 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile index 7450e191ee..da5307771b 100644 --- a/src/backend/nodes/Makefile +++ b/src/backend/nodes/Makefile @@ -57,7 +57,8 @@ node_headers = \ nodes/replnodes.h \ nodes/supportnodes.h \ nodes/value.h \ - utils/rel.h + utils/rel.h \ + nodes/bitmapset.h # see also catalog/Makefile for an explanation of these make rules diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e76fda8eba..1482019327 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -160,6 +160,17 @@ _copyExtensibleNode(const ExtensibleNode *from) return newnode; } +/* Custom copy routine for Node bitmapsets */ +static Bitmapset * +_copyBitmapset(const Bitmapset *from) +{ + Bitmapset *newnode = bms_copy(from); + + newnode->type = T_Bitmapset; + + return newnode; +} + /* * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 0373aa30fe..e8706c461a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -210,6 +210,12 @@ _equalList(const List *a, const List *b) return true; } +/* Custom equal routine for Node bitmapsets */ +static bool +_equalBitmapset(const Bitmapset *a, const Bitmapset *b) +{ + return bms_equal(a, b); +} /* * equal diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index 81b8c184a9..ccb5aff874 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -71,6 +71,7 @@ my @all_input_files = qw( nodes/supportnodes.h nodes/value.h utils/rel.h + nodes/bitmapset.h ); # Nodes from these input files are automatically treated as nodetag_only. diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b91e235423..d3beb907ea 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -328,6 +328,17 @@ outBitmapset(StringInfo str, const Bitmapset *bms) appendStringInfoChar(str, ')'); } +/* Custom write routine for Node bitmapsets */ +static void +_outBitmapset(StringInfo str, const Bitmapset *bms) +{ + Assert(IsA(bms, Bitmapset)); + WRITE_NODE_TYPE("BITMAPSET"); + + outBitmapset(str, bms); +} + + /* * Print the value of a Datum given its type. */ diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 75bf11c741..e5c993c90d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -230,6 +230,10 @@ _readBitmapset(void) result = bms_add_member(result, val); } + /* XXX maybe do `result = makeNode(Bitmapset);` at the top? */ + if (result) + result->type = T_Bitmapset; + return result; } diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 137b28323d..e5c1103316 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -337,7 +337,6 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } - /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h index 75b5ce1a8e..9046ca177f 100644 --- a/src/include/nodes/bitmapset.h +++ b/src/include/nodes/bitmapset.h @@ -20,6 +20,8 @@ #ifndef BITMAPSET_H #define BITMAPSET_H +#include "nodes/nodes.h" + /* * Forward decl to save including pg_list.h */ @@ -48,6 +50,9 @@ typedef int32 signedbitmapword; /* must be the matching signed type */ typedef struct Bitmapset { + pg_node_attr(custom_copy_equal, custom_read_write) + + NodeTag type; int nwords; /* number of words in array */ bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */ } Bitmapset; diff --git a/src/include/nodes/meson.build b/src/include/nodes/meson.build index b7df232081..94701af8e1 100644 --- a/src/include/nodes/meson.build +++ b/src/include/nodes/meson.build @@ -19,6 +19,7 @@ node_support_input_i = [ 'nodes/supportnodes.h', 'nodes/value.h', 'utils/rel.h', + 'nodes/bitmapset.h', ] node_support_input = [] -- 2.35.3
v21-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 120.6K) download From 3c0c6b3b16787fc427430fa610225dbf7b982760 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v21 2/4] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RTEPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add a copy of the original view RTE. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteDefine.c | 7 - src/backend/rewrite/rewriteHandler.c | 33 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 222 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 27 files changed, 729 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index cc9e39c4a5..b6c3749395 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2606,7 +2606,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2669,7 +2669,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6555,10 +6555,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6672,10 +6672,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b0747ce291..1d5f30443b 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 6f07ac2a9c..7e3d5e79bc 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,78 +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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -587,12 +515,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 5e9d226e54..ac2568e59a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -786,13 +786,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 7dbbbc629b..156c033bda 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before converting the RTE to become a SUBQUERY, store a copy for the + * executor to be able to lock the view relation and for the planner to be + * able to record the view relation OID in PlannedStmt.relationOids. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1858,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index a869321cdf..934a731205 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2225,7 +2225,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2241,7 +2241,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2257,7 +2257,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2273,7 +2273,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2291,7 +2291,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3283,7 +3283,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index fc2bd40be2..564a7ba1aa 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1623,7 +1623,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1675,7 +1675,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1691,7 +1691,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1707,7 +1707,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2192,15 +2192,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 346f594ad0..ecf4f65c12 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2479,8 +2479,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2491,8 +2491,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2518,8 +2518,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2531,8 +2531,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index bf4ff30d86..8f81a3098e 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1623,11 +1623,11 @@ returning pg_describe_object(classid, objid, objsubid) as obj, alter table tt14t drop column f3; -- column f3 is still in the view, sort of ... select pg_get_viewdef('tt14v', true); - pg_get_viewdef ---------------------------------- - SELECT t.f1, + - t."?dropped?column?" AS f3,+ - t.f4 + + pg_get_viewdef +------------------------------- + SELECT f1, + + "?dropped?column?" AS f3,+ + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1675,9 +1675,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1697,8 +1697,8 @@ create view tt14v as select t.f1, t.f4 from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1712,8 +1712,8 @@ alter table tt14t drop column f3; -- ok select pg_get_viewdef('tt14v', true); pg_get_viewdef ---------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1806,8 +1806,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -2054,7 +2054,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2106,19 +2106,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e2e62db6a2..fbb840e848 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 9dd137415e..19ef0a6b80 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1448,10 +1448,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1697,23 +1697,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1727,10 +1727,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1798,13 +1798,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1817,57 +1817,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1890,8 +1890,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1900,13 +1900,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2016,16 +2016,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2101,41 +2101,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2145,68 +2145,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2223,19 +2223,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2245,19 +2245,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2301,64 +2301,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2543,24 +2543,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3080,7 +3080,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3119,8 +3119,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3132,8 +3132,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3145,8 +3145,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3158,8 +3158,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 8b8eadd181..019c4726f6 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 170bea23c2..3d1d26aa39 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1212,10 +1212,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1238,10 +1238,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1264,10 +1264,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1290,10 +1290,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1316,10 +1316,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1341,10 +1341,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1353,10 +1353,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7f2e32d8b0..f40197acbf 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 948b4e702c..cc213523c0 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 5fd3886b5e..3986fc1706 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
v21-0004-Add-per-result-relation-extraUpdatedCols-to-Modi.patch (application/octet-stream, 28.4K) download From 345391612383e4d9a8ce210a787d65b92a1d2241 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Tue, 4 Oct 2022 20:54:03 +0900 Subject: [PATCH v21 4/4] Add per-result-relation extraUpdatedCols to ModifyTable In spirit of removing things from RangeTblEntry that are better carried by Query or PlannedStmt directly. --- src/backend/executor/execUtils.c | 9 +-- src/backend/executor/nodeModifyTable.c | 4 ++ src/backend/nodes/outfuncs.c | 1 - src/backend/nodes/readfuncs.c | 1 - src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 26 +++++++ src/backend/optimizer/prep/preptlist.c | 48 +++++++++++++ src/backend/optimizer/util/inherit.c | 89 +++++++++++++----------- src/backend/optimizer/util/pathnode.c | 4 ++ src/backend/replication/logical/worker.c | 6 +- src/backend/rewrite/rewriteHandler.c | 45 ------------ src/include/nodes/execnodes.h | 3 + src/include/nodes/parsenodes.h | 1 - src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 1 + src/include/optimizer/inherit.h | 3 + src/include/optimizer/pathnode.h | 1 + src/include/optimizer/prep.h | 4 ++ src/include/rewrite/rewriteHandler.h | 4 -- 19 files changed, 157 insertions(+), 102 deletions(-) diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 461230b011..ede4c98875 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1378,20 +1378,17 @@ ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { if (relinfo->ri_RangeTableIndex != 0) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + return relinfo->ri_extraUpdatedCols; } else if (relinfo->ri_RootResultRelInfo) { ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); if (relinfo->ri_RootToPartitionMap != NULL) return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); + rootRelInfo->ri_extraUpdatedCols); else - return rte->extraUpdatedCols; + return rootRelInfo->ri_extraUpdatedCols; } else return NULL; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 04454ad6e6..e034e3ceee 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3971,6 +3971,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) { resultRelInfo = &mtstate->resultRelInfo[i]; + if (node->extraUpdatedColsBitmaps) + resultRelInfo->ri_extraUpdatedCols = + list_nth(node->extraUpdatedColsBitmaps, i); + /* Let FDWs init themselves for foreign-table result rels */ if (!resultRelInfo->ri_usesFdwDirectModify && resultRelInfo->ri_FdwRoutine != NULL && diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index d3beb907ea..139d8e095f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -569,7 +569,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e5c993c90d..aa7077c7f8 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -536,7 +536,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f854855951..5c1c7aed2f 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -308,7 +308,7 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); @@ -2824,6 +2824,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->partColsUpdated, best_path->resultRelations, best_path->updateColnosLists, + best_path->extraUpdatedColsBitmaps, best_path->withCheckOptionLists, best_path->returningLists, best_path->rowMarks, @@ -6980,7 +6981,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -7049,6 +7050,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, node->exclRelTlist = onconflict->exclRelTlist; } node->updateColnosLists = updateColnosLists; + node->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; node->withCheckOptionLists = withCheckOptionLists; node->returningLists = returningLists; node->rowMarks = rowMarks; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9576b69f1a..cf5b895a28 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1746,6 +1746,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) Index rootRelation; List *resultRelations = NIL; List *updateColnosLists = NIL; + List *extraUpdatedColsBitmaps = NIL; List *withCheckOptionLists = NIL; List *returningLists = NIL; List *mergeActionLists = NIL; @@ -1779,15 +1780,33 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) if (parse->commandType == CMD_UPDATE) { List *update_colnos = root->update_colnos; + Bitmapset *extraUpdatedCols = root->extraUpdatedCols; if (this_result_rel != top_result_rel) + { update_colnos = adjust_inherited_attnums_multilevel(root, update_colnos, this_result_rel->relid, top_result_rel->relid); + extraUpdatedCols = + translate_col_privs_multilevel(root, this_result_rel, + top_result_rel, + extraUpdatedCols); + } updateColnosLists = lappend(updateColnosLists, update_colnos); + /* + * Make extraUpdatedCols bitmap look as a proper Node + * before adding into the List so that Node + * copy/write/read handle it correctly. + * + * XXX should be using makeNode(Bitmapset) somewhere? + */ + if (extraUpdatedCols) + extraUpdatedCols->type = T_Bitmapset; + extraUpdatedColsBitmaps = lappend(extraUpdatedColsBitmaps, + extraUpdatedCols); } if (parse->withCheckOptions) { @@ -1869,7 +1888,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1883,7 +1905,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) /* Single-relation INSERT/UPDATE/DELETE. */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1922,6 +1947,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) root->partColsUpdated, resultRelations, updateColnosLists, + extraUpdatedColsBitmaps, withCheckOptionLists, returningLists, rowMarks, diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index e5c1103316..ee5c9a1d82 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -43,6 +43,7 @@ #include "optimizer/prep.h" #include "optimizer/tlist.h" #include "parser/parse_coerce.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "utils/rel.h" @@ -106,6 +107,17 @@ preprocess_targetlist(PlannerInfo *root) else if (command_type == CMD_UPDATE) root->update_colnos = extract_update_targetlist_colnos(tlist); + /* Also populate extraUpdatedCols (for generated columns) */ + if (command_type == CMD_UPDATE) + { + RTEPermissionInfo *target_perminfo = + GetRTEPermissionInfo(parse->rtepermlist, target_rte); + + root->extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, + target_relation); + } + /* * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s) * needed to allow the executor to identify the rows to be updated or @@ -337,6 +349,42 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } +/* + * Return the indexes of any generated columns that depend on any columns + * mentioned in target_perminfo->updatedCols. + */ +Bitmapset * +get_extraUpdatedCols(Bitmapset *updatedCols, Relation target_relation) +{ + TupleDesc tupdesc = RelationGetDescr(target_relation); + TupleConstr *constr = tupdesc->constr; + Bitmapset *extraUpdatedCols = NULL; + + if (constr && constr->has_generated_stored) + { + for (int i = 0; i < constr->num_defval; i++) + { + AttrDefault *defval = &constr->defval[i]; + Node *expr; + Bitmapset *attrs_used = NULL; + + /* skip if not generated column */ + if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) + continue; + + /* identify columns this generated column depends on */ + expr = stringToNode(defval->adbin); + pull_varattnos(expr, 1, &attrs_used); + + if (bms_overlap(updatedCols, attrs_used)) + extraUpdatedCols = bms_add_member(extraUpdatedCols, + defval->adnum - FirstLowInvalidHeapAttributeNumber); + } + } + + return extraUpdatedCols; +} + /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 10c2aa13f6..65daec5f02 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -40,6 +40,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -148,6 +149,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, expand_partitioned_rtentry(root, rel, rte, rti, oldrelation, root_perminfo->updatedCols, + root->extraUpdatedCols, oldrc, lockmode); } else @@ -313,6 +315,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -343,7 +346,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -412,14 +415,18 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, { AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; child_updatedCols = translate_col_privs(parent_updatedCols, appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, childrel, child_updatedCols, + child_extraUpdatedCols, top_parentrc, lockmode); } @@ -456,7 +463,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -486,7 +492,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else @@ -553,13 +559,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* Translate the bitmapset of generated columns being updated. */ - if (childOID != parentOID) - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - else - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,28 +865,35 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, * 'top_parent_cols' to the columns numbers of a descendent relation * given by 'relid' */ -static Bitmapset * -translate_col_privs_recurse(PlannerInfo *root, Index relid, - Bitmapset *top_parent_cols, - Relids top_parent_relids) +Bitmapset * +translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel, + RelOptInfo *top_parent_rel, + Bitmapset *top_parent_cols) { + Bitmapset *result; AppendRelInfo *appinfo; + if (top_parent_cols == NULL) + return NULL; + + /* Recurse if immediate parent is not the top parent. */ + if (rel->parent != top_parent_rel) + { + if (rel->parent) + result = translate_col_privs_multilevel(root, rel->parent, + top_parent_rel, + top_parent_cols); + else + elog(ERROR, "rel with relid %u is not a child rel", rel->relid); + } + Assert(root->append_rel_array != NULL); - appinfo = root->append_rel_array[relid]; + appinfo = root->append_rel_array[rel->relid]; Assert(appinfo != NULL); - /* - * Must recurse if 'relid' doesn't appear to the parent's direct child, - * because appinfo->translated_vars maps between directly related parent - * and child relation pairs. - */ - if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) - translate_col_privs_recurse(root, appinfo->parent_relid, - top_parent_cols, - top_parent_relids); + result = translate_col_privs(top_parent_cols, appinfo->translated_vars); - return translate_col_privs(top_parent_cols, appinfo->translated_vars); + return result; } /* @@ -898,11 +904,14 @@ translate_col_privs_recurse(PlannerInfo *root, Index relid, Bitmapset * GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) { + Index use_relid; RangeTblEntry *rte; RTEPermissionInfo *perminfo; Bitmapset *updatedCols, *extraUpdatedCols; + Assert(root->parse->commandType == CMD_UPDATE); + if (!IS_SIMPLE_REL(rel)) return NULL; @@ -917,25 +926,27 @@ GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) * query, because only that one gets assigned a RTEPermissionInfo, and * translate the columns found therein to match the given relation. */ - if (rel->top_parent_relids != NULL) - rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), - root); - else - rte = planner_rt_fetch(rel->relid, root); - + use_relid = rel->top_parent_relids == NULL ? rel->relid : + bms_singleton_member(rel->top_parent_relids); + Assert(use_relid == root->parse->resultRelation); + rte = planner_rt_fetch(use_relid, root); Assert(rte->perminfoindex > 0); perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); - if (rel->top_parent_relids != NULL) - updatedCols = translate_col_privs_recurse(root, rel->relid, - perminfo->updatedCols, - rel->top_parent_relids); + if (use_relid != rel->relid) + { + RelOptInfo *top_parent_rel = find_base_rel(root, use_relid); + + updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + perminfo->updatedCols); + extraUpdatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + root->extraUpdatedCols); + } else + { updatedCols = perminfo->updatedCols; - - /* extraUpdatedCols can be obtained directly from the RTE. */ - rte = planner_rt_fetch(rel->relid, root); - extraUpdatedCols = rte->extraUpdatedCols; + extraUpdatedCols = root->extraUpdatedCols; + } return bms_union(updatedCols, extraUpdatedCols); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 70f61ae7b1..f70fe2736c 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3643,6 +3643,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel, * 'resultRelations' is an integer list of actual RT indexes of target rel(s) * 'updateColnosLists' is a list of UPDATE target column number lists * (one sublist per rel); or NIL if not an UPDATE + * 'extraUpdatedColsBitmaps' is a list of generated column attribute number + * bitmapsets (one bitmapset per rel); or NIL if not an UPDATE * 'withCheckOptionLists' is a list of WCO lists (one per rel) * 'returningLists' is a list of RETURNING tlists (one per rel) * 'rowMarks' is a list of PlanRowMarks (non-locking only) @@ -3658,6 +3660,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -3722,6 +3725,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, pathnode->partColsUpdated = partColsUpdated; pathnode->resultRelations = resultRelations; pathnode->updateColnosLists = updateColnosLists; + pathnode->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; pathnode->withCheckOptionLists = withCheckOptionLists; pathnode->returningLists = returningLists; pathnode->rowMarks = rowMarks; diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index e834130151..b75455382e 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "optimizer/prep.h" #include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" @@ -1791,7 +1792,6 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; RTEPermissionInfo *target_perminfo; MemoryContext oldctx; @@ -1840,7 +1840,6 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { @@ -1858,7 +1857,8 @@ apply_handle_update(StringInfo s) } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); + edata->targetRelInfo->ri_extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 156c033bda..d62d457fc0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1592,46 +1592,6 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, } -/* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * columns that depend on any columns mentioned in - * target_perminfo->updatedCols. - */ -void -fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation) -{ - TupleDesc tupdesc = RelationGetDescr(target_relation); - TupleConstr *constr = tupdesc->constr; - - target_rte->extraUpdatedCols = NULL; - - if (constr && constr->has_generated_stored) - { - for (int i = 0; i < constr->num_defval; i++) - { - AttrDefault *defval = &constr->defval[i]; - Node *expr; - Bitmapset *attrs_used = NULL; - - /* skip if not generated column */ - if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) - continue; - - /* identify columns this generated column depends on */ - expr = stringToNode(defval->adbin); - pull_varattnos(expr, 1, &attrs_used); - - if (bms_overlap(target_perminfo->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, - defval->adnum - FirstLowInvalidHeapAttributeNumber); - } - } -} - - /* * matchLocks - * match the list of locks and returns the matching rules @@ -3670,7 +3630,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; - RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3682,7 +3641,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); - rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3767,9 +3725,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) parsetree->override, rt_entry_relation, NULL, 0, NULL); - - /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index c32834a9e8..a85570b1de 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -471,6 +471,9 @@ typedef struct ResultRelInfo /* Have the projection and the slots above been initialized? */ bool ri_projectNewInfoValid; + /* generated column attribute numbers */ + Bitmapset *ri_extraUpdatedCols; + /* triggers to be fired, if any */ TriggerDesc *ri_TrigDesc; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 080680ecd0..118a150d3c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1152,7 +1152,6 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ List *securityQuals; /* security barrier quals to apply, if any */ } RangeTblEntry; diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 99c8d4f611..04c7403897 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -422,6 +422,8 @@ struct PlannerInfo */ List *update_colnos; + Bitmapset *extraUpdatedCols; + /* * Fields filled during create_plan() for use in setrefs.c */ @@ -2250,6 +2252,7 @@ typedef struct ModifyTablePath bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *rowMarks; /* PlanRowMarks (non-locking only) */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index aaff24256e..2f8c9f65cc 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -237,6 +237,7 @@ typedef struct ModifyTable bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *fdwPrivLists; /* per-target-table FDW private data lists */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index 9a4f86920c..a729401031 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -24,5 +24,8 @@ extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); +extern Bitmapset *translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel, + RelOptInfo *top_parent_rel, + Bitmapset *top_parent_cols); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 050f00e79a..fd16d94916 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -278,6 +278,7 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 5b4f350b33..92753c9670 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -16,6 +16,7 @@ #include "nodes/pathnodes.h" #include "nodes/plannodes.h" +#include "utils/relcache.h" /* @@ -39,6 +40,9 @@ extern void preprocess_targetlist(PlannerInfo *root); extern List *extract_update_targetlist_colnos(List *tlist); +extern Bitmapset *get_extraUpdatedCols(Bitmapset *updatedCols, + Relation target_relation); + extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex); /* diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 05c3680cd6..b4f96f298b 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,10 +24,6 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation); - extern Query *get_view_query(Relation view); extern const char *view_query_is_auto_updatable(Query *viewquery, bool check_cols); -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-07 06:49  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Amit Langote @ 2022-10-07 06:49 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Fri, Oct 7, 2022 at 1:25 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Fri, Oct 7, 2022 at 10:04 AM Amit Langote <amitlangote09@gmail.com> wrote:
> > On Thu, Oct 6, 2022 at 10:29 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > > Actually, List of Bitmapsets turned out to be something that doesn't
> > > just-work with our Node infrastructure, which I found out thanks to
> > > -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> > > first-class support for copy/equal/write/read support for Bitmapsets,
> > > such that writeNode() can write appropriately labeled versions of them
> > > and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
> > > didn't actually go ahead and make *all* Bitmapsets in the plan trees
> > > to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
> > > to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
> > > just use *_NODE_FIELD().
> >
> > All meson builds on the cfbot machines seem to have failed, maybe
> > because I didn't update src/include/nodes/meson.build to add
> > 'nodes/bitmapset.h' to the `node_support_input_i` collection.  Here's
> > an updated version assuming that's the problem.  (Will set up meson
> > builds on my machine to avoid this in the future.)
>
> And... noticed that a postgres_fdw test failed, because
> _readBitmapset() not having been changed to set NodeTag would
> "corrupt" any Bitmapsets that were created with it set.

Broke the other cases while fixing the above.  Attaching a new version
again.  In the latest version, I'm setting Bitmapset.type by hand with
an XXX comment nearby saying that it would be nice to change that to
makeNode(Bitmapset), which I know sounds pretty ad-hoc.

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

Attachments: download all (4 files, .tar.gz)

v22-0004-Add-per-result-relation-extraUpdatedCols-to-Modi.patch (application/octet-stream, 28.4K) download From 345391612383e4d9a8ce210a787d65b92a1d2241 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Tue, 4 Oct 2022 20:54:03 +0900 Subject: [PATCH v22 4/4] Add per-result-relation extraUpdatedCols to ModifyTable In spirit of removing things from RangeTblEntry that are better carried by Query or PlannedStmt directly. --- src/backend/executor/execUtils.c | 9 +-- src/backend/executor/nodeModifyTable.c | 4 ++ src/backend/nodes/outfuncs.c | 1 - src/backend/nodes/readfuncs.c | 1 - src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 26 +++++++ src/backend/optimizer/prep/preptlist.c | 48 +++++++++++++ src/backend/optimizer/util/inherit.c | 89 +++++++++++++----------- src/backend/optimizer/util/pathnode.c | 4 ++ src/backend/replication/logical/worker.c | 6 +- src/backend/rewrite/rewriteHandler.c | 45 ------------ src/include/nodes/execnodes.h | 3 + src/include/nodes/parsenodes.h | 1 - src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 1 + src/include/optimizer/inherit.h | 3 + src/include/optimizer/pathnode.h | 1 + src/include/optimizer/prep.h | 4 ++ src/include/rewrite/rewriteHandler.h | 4 -- 19 files changed, 157 insertions(+), 102 deletions(-) diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 461230b011..ede4c98875 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1378,20 +1378,17 @@ ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { if (relinfo->ri_RangeTableIndex != 0) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + return relinfo->ri_extraUpdatedCols; } else if (relinfo->ri_RootResultRelInfo) { ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); if (relinfo->ri_RootToPartitionMap != NULL) return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); + rootRelInfo->ri_extraUpdatedCols); else - return rte->extraUpdatedCols; + return rootRelInfo->ri_extraUpdatedCols; } else return NULL; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 04454ad6e6..e034e3ceee 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3971,6 +3971,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) { resultRelInfo = &mtstate->resultRelInfo[i]; + if (node->extraUpdatedColsBitmaps) + resultRelInfo->ri_extraUpdatedCols = + list_nth(node->extraUpdatedColsBitmaps, i); + /* Let FDWs init themselves for foreign-table result rels */ if (!resultRelInfo->ri_usesFdwDirectModify && resultRelInfo->ri_FdwRoutine != NULL && diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index d3beb907ea..139d8e095f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -569,7 +569,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e5c993c90d..aa7077c7f8 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -536,7 +536,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f854855951..5c1c7aed2f 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -308,7 +308,7 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); @@ -2824,6 +2824,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->partColsUpdated, best_path->resultRelations, best_path->updateColnosLists, + best_path->extraUpdatedColsBitmaps, best_path->withCheckOptionLists, best_path->returningLists, best_path->rowMarks, @@ -6980,7 +6981,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, Index nominalRelation, Index rootRelation, bool partColsUpdated, List *resultRelations, - List *updateColnosLists, + List *updateColnosLists, List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -7049,6 +7050,7 @@ make_modifytable(PlannerInfo *root, Plan *subplan, node->exclRelTlist = onconflict->exclRelTlist; } node->updateColnosLists = updateColnosLists; + node->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; node->withCheckOptionLists = withCheckOptionLists; node->returningLists = returningLists; node->rowMarks = rowMarks; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9576b69f1a..cf5b895a28 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1746,6 +1746,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) Index rootRelation; List *resultRelations = NIL; List *updateColnosLists = NIL; + List *extraUpdatedColsBitmaps = NIL; List *withCheckOptionLists = NIL; List *returningLists = NIL; List *mergeActionLists = NIL; @@ -1779,15 +1780,33 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) if (parse->commandType == CMD_UPDATE) { List *update_colnos = root->update_colnos; + Bitmapset *extraUpdatedCols = root->extraUpdatedCols; if (this_result_rel != top_result_rel) + { update_colnos = adjust_inherited_attnums_multilevel(root, update_colnos, this_result_rel->relid, top_result_rel->relid); + extraUpdatedCols = + translate_col_privs_multilevel(root, this_result_rel, + top_result_rel, + extraUpdatedCols); + } updateColnosLists = lappend(updateColnosLists, update_colnos); + /* + * Make extraUpdatedCols bitmap look as a proper Node + * before adding into the List so that Node + * copy/write/read handle it correctly. + * + * XXX should be using makeNode(Bitmapset) somewhere? + */ + if (extraUpdatedCols) + extraUpdatedCols->type = T_Bitmapset; + extraUpdatedColsBitmaps = lappend(extraUpdatedColsBitmaps, + extraUpdatedCols); } if (parse->withCheckOptions) { @@ -1869,7 +1888,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1883,7 +1905,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) /* Single-relation INSERT/UPDATE/DELETE. */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) + { updateColnosLists = list_make1(root->update_colnos); + extraUpdatedColsBitmaps = list_make1(root->extraUpdatedCols); + } if (parse->withCheckOptions) withCheckOptionLists = list_make1(parse->withCheckOptions); if (parse->returningList) @@ -1922,6 +1947,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) root->partColsUpdated, resultRelations, updateColnosLists, + extraUpdatedColsBitmaps, withCheckOptionLists, returningLists, rowMarks, diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index e5c1103316..ee5c9a1d82 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -43,6 +43,7 @@ #include "optimizer/prep.h" #include "optimizer/tlist.h" #include "parser/parse_coerce.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "utils/rel.h" @@ -106,6 +107,17 @@ preprocess_targetlist(PlannerInfo *root) else if (command_type == CMD_UPDATE) root->update_colnos = extract_update_targetlist_colnos(tlist); + /* Also populate extraUpdatedCols (for generated columns) */ + if (command_type == CMD_UPDATE) + { + RTEPermissionInfo *target_perminfo = + GetRTEPermissionInfo(parse->rtepermlist, target_rte); + + root->extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, + target_relation); + } + /* * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s) * needed to allow the executor to identify the rows to be updated or @@ -337,6 +349,42 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } +/* + * Return the indexes of any generated columns that depend on any columns + * mentioned in target_perminfo->updatedCols. + */ +Bitmapset * +get_extraUpdatedCols(Bitmapset *updatedCols, Relation target_relation) +{ + TupleDesc tupdesc = RelationGetDescr(target_relation); + TupleConstr *constr = tupdesc->constr; + Bitmapset *extraUpdatedCols = NULL; + + if (constr && constr->has_generated_stored) + { + for (int i = 0; i < constr->num_defval; i++) + { + AttrDefault *defval = &constr->defval[i]; + Node *expr; + Bitmapset *attrs_used = NULL; + + /* skip if not generated column */ + if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) + continue; + + /* identify columns this generated column depends on */ + expr = stringToNode(defval->adbin); + pull_varattnos(expr, 1, &attrs_used); + + if (bms_overlap(updatedCols, attrs_used)) + extraUpdatedCols = bms_add_member(extraUpdatedCols, + defval->adnum - FirstLowInvalidHeapAttributeNumber); + } + } + + return extraUpdatedCols; +} + /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 10c2aa13f6..65daec5f02 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -40,6 +40,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -148,6 +149,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, expand_partitioned_rtentry(root, rel, rte, rti, oldrelation, root_perminfo->updatedCols, + root->extraUpdatedCols, oldrc, lockmode); } else @@ -313,6 +315,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -343,7 +346,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * There shouldn't be any generated columns in the partition key. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -412,14 +415,18 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, { AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; Bitmapset *child_updatedCols; + Bitmapset *child_extraUpdatedCols; child_updatedCols = translate_col_privs(parent_updatedCols, appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, childrel, child_updatedCols, + child_extraUpdatedCols, top_parentrc, lockmode); } @@ -456,7 +463,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -486,7 +492,7 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else @@ -553,13 +559,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* Translate the bitmapset of generated columns being updated. */ - if (childOID != parentOID) - childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, - appinfo->translated_vars); - else - childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. @@ -866,28 +865,35 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, * 'top_parent_cols' to the columns numbers of a descendent relation * given by 'relid' */ -static Bitmapset * -translate_col_privs_recurse(PlannerInfo *root, Index relid, - Bitmapset *top_parent_cols, - Relids top_parent_relids) +Bitmapset * +translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel, + RelOptInfo *top_parent_rel, + Bitmapset *top_parent_cols) { + Bitmapset *result; AppendRelInfo *appinfo; + if (top_parent_cols == NULL) + return NULL; + + /* Recurse if immediate parent is not the top parent. */ + if (rel->parent != top_parent_rel) + { + if (rel->parent) + result = translate_col_privs_multilevel(root, rel->parent, + top_parent_rel, + top_parent_cols); + else + elog(ERROR, "rel with relid %u is not a child rel", rel->relid); + } + Assert(root->append_rel_array != NULL); - appinfo = root->append_rel_array[relid]; + appinfo = root->append_rel_array[rel->relid]; Assert(appinfo != NULL); - /* - * Must recurse if 'relid' doesn't appear to the parent's direct child, - * because appinfo->translated_vars maps between directly related parent - * and child relation pairs. - */ - if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) - translate_col_privs_recurse(root, appinfo->parent_relid, - top_parent_cols, - top_parent_relids); + result = translate_col_privs(top_parent_cols, appinfo->translated_vars); - return translate_col_privs(top_parent_cols, appinfo->translated_vars); + return result; } /* @@ -898,11 +904,14 @@ translate_col_privs_recurse(PlannerInfo *root, Index relid, Bitmapset * GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) { + Index use_relid; RangeTblEntry *rte; RTEPermissionInfo *perminfo; Bitmapset *updatedCols, *extraUpdatedCols; + Assert(root->parse->commandType == CMD_UPDATE); + if (!IS_SIMPLE_REL(rel)) return NULL; @@ -917,25 +926,27 @@ GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) * query, because only that one gets assigned a RTEPermissionInfo, and * translate the columns found therein to match the given relation. */ - if (rel->top_parent_relids != NULL) - rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), - root); - else - rte = planner_rt_fetch(rel->relid, root); - + use_relid = rel->top_parent_relids == NULL ? rel->relid : + bms_singleton_member(rel->top_parent_relids); + Assert(use_relid == root->parse->resultRelation); + rte = planner_rt_fetch(use_relid, root); Assert(rte->perminfoindex > 0); perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); - if (rel->top_parent_relids != NULL) - updatedCols = translate_col_privs_recurse(root, rel->relid, - perminfo->updatedCols, - rel->top_parent_relids); + if (use_relid != rel->relid) + { + RelOptInfo *top_parent_rel = find_base_rel(root, use_relid); + + updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + perminfo->updatedCols); + extraUpdatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + root->extraUpdatedCols); + } else + { updatedCols = perminfo->updatedCols; - - /* extraUpdatedCols can be obtained directly from the RTE. */ - rte = planner_rt_fetch(rel->relid, root); - extraUpdatedCols = rte->extraUpdatedCols; + extraUpdatedCols = root->extraUpdatedCols; + } return bms_union(updatedCols, extraUpdatedCols); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 70f61ae7b1..f70fe2736c 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3643,6 +3643,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel, * 'resultRelations' is an integer list of actual RT indexes of target rel(s) * 'updateColnosLists' is a list of UPDATE target column number lists * (one sublist per rel); or NIL if not an UPDATE + * 'extraUpdatedColsBitmaps' is a list of generated column attribute number + * bitmapsets (one bitmapset per rel); or NIL if not an UPDATE * 'withCheckOptionLists' is a list of WCO lists (one per rel) * 'returningLists' is a list of RETURNING tlists (one per rel) * 'rowMarks' is a list of PlanRowMarks (non-locking only) @@ -3658,6 +3660,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam) @@ -3722,6 +3725,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, pathnode->partColsUpdated = partColsUpdated; pathnode->resultRelations = resultRelations; pathnode->updateColnosLists = updateColnosLists; + pathnode->extraUpdatedColsBitmaps = extraUpdatedColsBitmaps; pathnode->withCheckOptionLists = withCheckOptionLists; pathnode->returningLists = returningLists; pathnode->rowMarks = rowMarks; diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index e834130151..b75455382e 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "optimizer/prep.h" #include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" @@ -1791,7 +1792,6 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; RTEPermissionInfo *target_perminfo; MemoryContext oldctx; @@ -1840,7 +1840,6 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { @@ -1858,7 +1857,8 @@ apply_handle_update(StringInfo s) } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); + edata->targetRelInfo->ri_extraUpdatedCols = + get_extraUpdatedCols(target_perminfo->updatedCols, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 156c033bda..d62d457fc0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1592,46 +1592,6 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, } -/* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * columns that depend on any columns mentioned in - * target_perminfo->updatedCols. - */ -void -fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation) -{ - TupleDesc tupdesc = RelationGetDescr(target_relation); - TupleConstr *constr = tupdesc->constr; - - target_rte->extraUpdatedCols = NULL; - - if (constr && constr->has_generated_stored) - { - for (int i = 0; i < constr->num_defval; i++) - { - AttrDefault *defval = &constr->defval[i]; - Node *expr; - Bitmapset *attrs_used = NULL; - - /* skip if not generated column */ - if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) - continue; - - /* identify columns this generated column depends on */ - expr = stringToNode(defval->adbin); - pull_varattnos(expr, 1, &attrs_used); - - if (bms_overlap(target_perminfo->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, - defval->adnum - FirstLowInvalidHeapAttributeNumber); - } - } -} - - /* * matchLocks - * match the list of locks and returns the matching rules @@ -3670,7 +3630,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; - RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3682,7 +3641,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); - rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3767,9 +3725,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) parsetree->override, rt_entry_relation, NULL, 0, NULL); - - /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index c32834a9e8..a85570b1de 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -471,6 +471,9 @@ typedef struct ResultRelInfo /* Have the projection and the slots above been initialized? */ bool ri_projectNewInfoValid; + /* generated column attribute numbers */ + Bitmapset *ri_extraUpdatedCols; + /* triggers to be fired, if any */ TriggerDesc *ri_TrigDesc; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 080680ecd0..118a150d3c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1152,7 +1152,6 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ List *securityQuals; /* security barrier quals to apply, if any */ } RangeTblEntry; diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 99c8d4f611..04c7403897 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -422,6 +422,8 @@ struct PlannerInfo */ List *update_colnos; + Bitmapset *extraUpdatedCols; + /* * Fields filled during create_plan() for use in setrefs.c */ @@ -2250,6 +2252,7 @@ typedef struct ModifyTablePath bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *rowMarks; /* PlanRowMarks (non-locking only) */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index aaff24256e..2f8c9f65cc 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -237,6 +237,7 @@ typedef struct ModifyTable bool partColsUpdated; /* some part key in hierarchy updated? */ List *resultRelations; /* integer list of RT indexes */ List *updateColnosLists; /* per-target-table update_colnos lists */ + List *extraUpdatedColsBitmaps; /* per-target-table extraUpdatedCols bitmaps */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *fdwPrivLists; /* per-target-table FDW private data lists */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index 9a4f86920c..a729401031 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -24,5 +24,8 @@ extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); +extern Bitmapset *translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel, + RelOptInfo *top_parent_rel, + Bitmapset *top_parent_cols); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 050f00e79a..fd16d94916 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -278,6 +278,7 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root, bool partColsUpdated, List *resultRelations, List *updateColnosLists, + List *extraUpdatedColsBitmaps, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionLists, int epqParam); diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 5b4f350b33..92753c9670 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -16,6 +16,7 @@ #include "nodes/pathnodes.h" #include "nodes/plannodes.h" +#include "utils/relcache.h" /* @@ -39,6 +40,9 @@ extern void preprocess_targetlist(PlannerInfo *root); extern List *extract_update_targetlist_colnos(List *tlist); +extern Bitmapset *get_extraUpdatedCols(Bitmapset *updatedCols, + Relation target_relation); + extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex); /* diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 05c3680cd6..b4f96f298b 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,10 +24,6 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, - RTEPermissionInfo *target_perminfo, - Relation target_relation); - extern Query *get_view_query(Relation view); extern const char *view_query_is_auto_updatable(Query *viewquery, bool check_cols); -- 2.35.3
v22-0003-Allow-adding-Bitmapsets-as-Nodes-into-plan-trees.patch (application/octet-stream, 5.5K) download From be35773af5f29b9406d052238b79dc0a08dcef83 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Thu, 6 Oct 2022 17:31:37 +0900 Subject: [PATCH v22 3/4] Allow adding Bitmapsets as Nodes into plan trees Note that this only adds some infrastructure bits and none of the existing bitmapsets that are added to plan trees have been changed to instead add the Node version. So, the plan trees, or really the bitmapsets contained in them, look the same as before as far as Node write/read functionality is concerned. This is needed, because it is not currently possible to write and then read back Bitmapsets that are not direct members of write/read capable Nodes; for example, if one needs to add a List of Bitmapsets to a plan tree. The most straightforward way to do that is to make Bitmapsets be written with outNode() and read with nodeRead(). --- src/backend/nodes/Makefile | 3 ++- src/backend/nodes/copyfuncs.c | 11 +++++++++++ src/backend/nodes/equalfuncs.c | 6 ++++++ src/backend/nodes/gen_node_support.pl | 1 + src/backend/nodes/outfuncs.c | 11 +++++++++++ src/backend/nodes/readfuncs.c | 4 ++++ src/backend/optimizer/prep/preptlist.c | 1 - src/include/nodes/bitmapset.h | 5 +++++ src/include/nodes/meson.build | 1 + 9 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile index 7450e191ee..da5307771b 100644 --- a/src/backend/nodes/Makefile +++ b/src/backend/nodes/Makefile @@ -57,7 +57,8 @@ node_headers = \ nodes/replnodes.h \ nodes/supportnodes.h \ nodes/value.h \ - utils/rel.h + utils/rel.h \ + nodes/bitmapset.h # see also catalog/Makefile for an explanation of these make rules diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e76fda8eba..1482019327 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -160,6 +160,17 @@ _copyExtensibleNode(const ExtensibleNode *from) return newnode; } +/* Custom copy routine for Node bitmapsets */ +static Bitmapset * +_copyBitmapset(const Bitmapset *from) +{ + Bitmapset *newnode = bms_copy(from); + + newnode->type = T_Bitmapset; + + return newnode; +} + /* * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 0373aa30fe..e8706c461a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -210,6 +210,12 @@ _equalList(const List *a, const List *b) return true; } +/* Custom equal routine for Node bitmapsets */ +static bool +_equalBitmapset(const Bitmapset *a, const Bitmapset *b) +{ + return bms_equal(a, b); +} /* * equal diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index 81b8c184a9..ccb5aff874 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -71,6 +71,7 @@ my @all_input_files = qw( nodes/supportnodes.h nodes/value.h utils/rel.h + nodes/bitmapset.h ); # Nodes from these input files are automatically treated as nodetag_only. diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b91e235423..d3beb907ea 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -328,6 +328,17 @@ outBitmapset(StringInfo str, const Bitmapset *bms) appendStringInfoChar(str, ')'); } +/* Custom write routine for Node bitmapsets */ +static void +_outBitmapset(StringInfo str, const Bitmapset *bms) +{ + Assert(IsA(bms, Bitmapset)); + WRITE_NODE_TYPE("BITMAPSET"); + + outBitmapset(str, bms); +} + + /* * Print the value of a Datum given its type. */ diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 75bf11c741..e5c993c90d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -230,6 +230,10 @@ _readBitmapset(void) result = bms_add_member(result, val); } + /* XXX maybe do `result = makeNode(Bitmapset);` at the top? */ + if (result) + result->type = T_Bitmapset; + return result; } diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 137b28323d..e5c1103316 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -337,7 +337,6 @@ extract_update_targetlist_colnos(List *tlist) return update_colnos; } - /***************************************************************************** * * TARGETLIST EXPANSION diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h index 75b5ce1a8e..9046ca177f 100644 --- a/src/include/nodes/bitmapset.h +++ b/src/include/nodes/bitmapset.h @@ -20,6 +20,8 @@ #ifndef BITMAPSET_H #define BITMAPSET_H +#include "nodes/nodes.h" + /* * Forward decl to save including pg_list.h */ @@ -48,6 +50,9 @@ typedef int32 signedbitmapword; /* must be the matching signed type */ typedef struct Bitmapset { + pg_node_attr(custom_copy_equal, custom_read_write) + + NodeTag type; int nwords; /* number of words in array */ bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */ } Bitmapset; diff --git a/src/include/nodes/meson.build b/src/include/nodes/meson.build index b7df232081..94701af8e1 100644 --- a/src/include/nodes/meson.build +++ b/src/include/nodes/meson.build @@ -19,6 +19,7 @@ node_support_input_i = [ 'nodes/supportnodes.h', 'nodes/value.h', 'utils/rel.h', + 'nodes/bitmapset.h', ] node_support_input = [] -- 2.35.3
v22-0001-Rework-query-relation-permission-checking.patch (application/octet-stream, 145.6K) download From fe5ee4bc42b1d5e3ee506e8b5503650d7c376fc2 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v22 1/4] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table looking for relations that need to have permissions checked. This can make the permission checking part of the executor initialization needlessly expensive when many inheritance children are present in the range range. While the permissions need not be checked on the individual child relations, the executor still must visit every range table entry to filter them out. This commit moves the permission checking information out of the range table entries into a new plan node called RTEPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RTEPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the correponding RTEPermissionInfo's index in the query's list of the latter. --- contrib/postgres_fdw/postgres_fdw.c | 82 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 12 +- contrib/sepgsql/sepgsql.h | 3 +- src/backend/access/common/attmap.c | 14 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 17 +- src/backend/commands/copyfrom.c | 11 +- src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 115 +++++------ src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 133 +++++++++---- src/backend/nodes/outfuncs.c | 6 +- src/backend/nodes/readfuncs.c | 6 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 43 +++- src/backend/optimizer/plan/subselect.c | 7 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 155 +++++++++++---- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 65 +++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 175 +++++++++-------- src/backend/parser/parse_target.c | 18 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 11 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 27 +-- src/backend/rewrite/rewriteHandler.c | 183 ++++++++---------- src/backend/rewrite/rewriteManip.c | 25 +++ src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 19 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 11 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 95 +++++---- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 9 +- src/include/parser/parse_relation.h | 4 + src/include/rewrite/rewriteHandler.h | 1 + src/include/rewrite/rewriteManip.h | 2 + .../modules/test_oat_hooks/test_oat_hooks.c | 12 +- 54 files changed, 951 insertions(+), 563 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 8d013f5b1a..de913fc4ae 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1510,16 +1512,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1811,7 +1812,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1890,6 +1892,36 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1901,6 +1933,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1908,6 +1941,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1931,6 +1965,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1942,7 +1977,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2145,6 +2181,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2222,6 +2259,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2238,7 +2277,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2624,7 +2664,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2644,13 +2683,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3953,12 +3991,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3970,12 +4008,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..c4e071b0ea 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -23,6 +23,7 @@ #include "commands/tablecmds.h" #include "executor/executor.h" #include "nodes/bitmapset.h" +#include "parser/parsetree.h" #include "sepgsql.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -277,38 +278,33 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, + bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, rtepermlist) { - RangeTblEntry *rte = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +335,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 87fdd972c2..129442b96e 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -35,7 +35,7 @@ PG_MODULE_MAGIC; * Saved hook entries (if stacked) */ static object_access_hook_type next_object_access_hook = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *rangeTbls, List *rtepermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (rangeTbls, rtepermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(rangeTbls, rtepermlist, abort)) return false; return true; @@ -471,8 +471,8 @@ _PG_init(void) object_access_hook = sepgsql_object_access; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = sepgsql_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = sepgsql_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f2a2c795bf..7aa6df92ec 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object, /* * dml.c */ -extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation); +extern bool sepgsql_dml_privileges(List *rangeTabls, List *rtepermlist, + bool abort_on_violation); /* * database.c diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..1e65d8a120 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,15 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error; AttrMap.attnums[] entry for such an + * outdesc column will be 0 in that case. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +240,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +262,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 49924e476a..5a62d5641d 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +155,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +177,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 175aa837f2..575ac5c81d 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -657,6 +657,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the RTEPermissionInfos into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_rtepermlist = cstate->rtepermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1380,9 +1386,12 @@ BeginCopyFrom(ParseState *pstate, initStringInfo(&cstate->attribute_buf); - /* Assign range table, we'll need it in CopyFrom. */ + /* Assign range table and rtepermlist, we'll need them in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->rtepermlist = pstate->p_rtepermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index fd56066c13..622b574860 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1288,7 +1288,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1f774ac065..c2f50ae215 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1206,7 +1206,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9647,7 +9648,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9814,7 +9816,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10020,7 +10023,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10198,7 +10202,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12303,7 +12308,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18024,7 +18030,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18951,7 +18958,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index b5a0fc02e5..6f07ac2a9c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -411,10 +411,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d78862e660..c43d2215b3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,8 +74,8 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ -ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; +/* Hook for plugin to get control in ExecCheckPermissions() */ +ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook = NULL; /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +565,63 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rtepermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } - if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, - ereport_on_violation); + if (ExecutorCheckPermissions_hook) + result = (*ExecutorCheckPermissions_hook) (rangeTable, rtepermlist, + ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->rtepermlist, true); + estate->es_rtepermlist = plannedstmt->rtepermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 99512826c5..c1c5439fa1 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->rtepermlist = estate->es_rtepermlist; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 40e3c07693..b7b57ec404 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -780,7 +782,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -878,7 +881,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1140,7 +1144,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..461230b011 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent, something that's allowed with + * traditional inheritance, are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRTEPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RTEPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RTEPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,41 +1318,64 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 64c65f060b..b91e235423 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -504,6 +504,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -557,11 +558,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index b4ff855f7c..75bf11c741 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -468,6 +468,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -531,11 +532,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ab4d8e201d..f854855951 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5794,7 +5797,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 5d0fd6e072..9576b69f1a 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrtepermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrtepermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -520,6 +523,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->rtepermlist = glob->finalrtepermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6210,6 +6214,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6337,6 +6342,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..a26aa36048 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -355,6 +355,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rtepermlist. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -370,14 +373,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ + rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) + { + /* + * Update perminfoindex, if any, to reflect the correponding + * RTEPermissionInfo's position in the flattened list. + */ + if (rte->perminfoindex > 0) + rte->perminfoindex += list_length(glob->finalrtepermlist); + add_rte_to_flat_rtable(glob, rte); + } + + rti++; } + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + root->parse->rtepermlist); + /* * If there are any dead subqueries, they are not referenced in the Plan * tree, so we must add RTEs contained in them to the flattened rtable @@ -450,6 +468,15 @@ flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) flatten_rtes_walker, (void *) glob, QTW_EXAMINE_RTES_BEFORE); + + /* + * Now add the subquery's RTEPermissionInfos too. flatten_rtes_walker() + * should already have updated the perminfoindex in the RTEs in the + * subquery to reflect the corresponding RTEPermissionInfos' position in + * finalrtepermlist. + */ + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + rte->subquery->rtepermlist); } static bool @@ -463,7 +490,15 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) + { + /* + * The correponding RTEPermissionInfo will get added to + * finalrtepermlist, so adjust perminfoindex accordingly. + */ + Assert(rte->perminfoindex > 0); + rte->perminfoindex += list_length(glob->finalrtepermlist); add_rte_to_flat_rtable(glob, rte); + } return false; } if (IsA(node, Query)) @@ -483,10 +518,10 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo for executor-startup + * permission checking. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..a61082d27c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,6 +1496,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, + subselect->rtable); + /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 41c7066d90..607f7ae907 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1205,6 +1198,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, + subquery->rtable); + /* * Now append the adjusted rtable entries to upper query. (We hold off * until after fixing the upper rtable entries; no point in running that @@ -1345,6 +1345,12 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, + subquery->rtepermlist, rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index cf7691a474..10c2aa13f6 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *root_perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +312,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +332,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains the root partrel's + * updatedCols translated to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +409,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +468,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +491,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,33 +553,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ + /* Translate the bitmapset of generated columns being updated. */ if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); - } else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which @@ -866,3 +859,83 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * We need to get the updatedCols bitmapset from the relation's + * RTEPermissionInfo. + * + * If it's a simple "base" rel, can just fetch its RTEPermissionInfo that + * must always be present; this cannot get called on non-RELATION RTEs. + * + * For "other" rels, must look up the root parent relation mentioned in the + * query, because only that one gets assigned a RTEPermissionInfo, and + * translate the columns found therein to match the given relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + Assert(rte->perminfoindex > 0); + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + if (rel->top_parent_relids != NULL) + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + else + updatedCols = perminfo->updatedCols; + + /* extraUpdatedCols can be obtained directly from the RTE. */ + rte = planner_rt_fetch(rel->relid, root); + extraUpdatedCols = rte->extraUpdatedCols; + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index edcdd0a360..7711075ac2 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -236,7 +237,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though + * only the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..25324d9486 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; + List *sub_rtepermlist; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -594,10 +596,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing - * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in - * that case we want the rule's OLD and NEW rtable entries to appear as - * part of the SELECT's rtable, not as outer references for it. (Kluge!) + * INSERT/SELECT, arrange to pass the rangetable/rtepermlist/namespace down + * to the SELECT. This can only happen if we are inside a CREATE RULE, + * and in that case we want the rule's OLD and NEW rtable entries to appear + * as part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ @@ -605,6 +607,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; + sub_rtepermlist = pstate->p_rtepermlist; + pstate->p_rtepermlist = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } @@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_rtepermlist = sub_rtepermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +899,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +915,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1102,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2432,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2444,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2485,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3252,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3341,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index c2b5474f5f..95cfe7d2b5 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3224,16 +3224,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RTEPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index bb9d76306b..5781a4b995 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -210,6 +210,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; /* * Transform the join condition. This includes references to the target @@ -282,7 +283,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -341,7 +342,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -355,8 +356,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 81f9ae2f02..4dc8d7ecf5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,10 +1021,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1238,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RTEPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1280,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1424,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1461,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1470,8 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1485,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1522,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1546,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1555,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1570,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1644,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1974,20 +1969,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1999,7 +1987,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2066,20 +2054,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2154,19 +2135,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2251,19 +2226,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2278,6 +2247,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2401,19 +2371,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2527,16 +2491,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2548,7 +2509,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3173,6 +3134,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3190,7 +3152,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3742,3 +3707,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRTEPermissionInfo + * Creates RTEPermissionInfo for a given RTE and adds it into the + * provided list + * + * Returns the RTEPermissionInfo and sets rte->perminfoindex. + */ +RTEPermissionInfo * +AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex == 0); + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RTEPermissionInfo); + perminfo->relid = rte->relid; + perminfo->inh = rte->inh; + /* Other information is set by fetching the node as and where needed. */ + + *rtepermlist = lappend(*rtepermlist, perminfo); + + /* Note its index. */ + rte->perminfoindex = list_length(*rtepermlist); + + return perminfo; +} + +/* + * GetRTEPermissionInfo + * Find RTEPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RTEPermissionInfo * +GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + rte->perminfoindex, rte->relid); + perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL); + if (perminfo->relid != rte->relid) + elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bd8057bc3e..0415fcec7e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1132,7 +1132,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1367,6 +1367,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1381,7 +1382,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1414,11 +1418,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * with its RTEPermissionInfo's selectedCols bitmap showing the whole row + * as needing select permission, as well as the individual columns. + * However, we can only get here for weird notations like (table.*).*, so + * it's not worth trying to clean up --- arguably, the permissions marking + * is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index bd068bba05..f542b43549 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1232,7 +1232,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3022,9 +3023,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3080,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->rtepermlist = pstate->p_rtepermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3122,8 +3121,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 207a5805ba..e834130151 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -492,6 +493,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRTEPermissionInfo(&estate->es_rtepermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1789,6 +1792,7 @@ apply_handle_update(StringInfo s) bool has_oldtup; TupleTableSlot *remoteslot; RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1837,6 +1841,7 @@ apply_handle_update(StringInfo s) * on the subscriber, since we are not touching those. */ target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1846,14 +1851,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2ecaa5b907..f2128190d8 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1125,7 +1125,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 213eabfbb9..5e9d226e54 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -785,14 +785,14 @@ 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 rtable entries. + * 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 will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * 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) @@ -819,18 +819,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RTEPermissionInfos for this query. */ + foreach(l, qry->rtepermlist) + { + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d02fd83c0a..7dbbbc629b 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -352,6 +352,7 @@ rewriteRuleAction(Query *parsetree, Query *sub_action; Query **sub_action_ptr; acquireLocksOnSubLinks_context context; + List *query_rtable; context.for_execute = true; @@ -394,32 +395,35 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. + * Also merge RTEPermissionInfo lists to ensure that all permissions are + * checked correctly. * * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. - * - * NOTE: because planner will destructively alter rtable, we must ensure - * that rule action's rtable is separate and shares no substructure with - * the main rtable. Hence do a deep copy here. + * NOTE: because planner will destructively alter rtable and rtepermlist, + * we must ensure that rule action's lists are separate and shares no + * substructure with the main query's lists. Hence do a deep copy here + * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), + * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtable = list_concat(copyObject(parsetree->rtable), - sub_action->rtable); + sub_action->rtepermlist = copyObject(sub_action->rtepermlist); + query_rtable = copyObject(parsetree->rtable); + ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, + parsetree->rtepermlist, query_rtable); + sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1590,10 +1594,13 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; @@ -1616,7 +1623,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); @@ -1707,8 +1714,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1749,18 +1755,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1824,12 +1818,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1847,28 +1835,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1897,8 +1866,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3039,6 +3012,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RTEPermissionInfo *view_perminfo; + RTEPermissionInfo *base_perminfo; + RTEPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3175,6 +3151,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); Assert(base_rte->rtekind == RTE_RELATION); + base_perminfo = GetRTEPermissionInfo(viewquery->rtepermlist, base_rte); /* * Up to now, the base relation hasn't been touched at all in our query. @@ -3246,57 +3223,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RTEPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRTEPermissionInfo(&parsetree->rtepermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3400,7 +3379,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3411,8 +3390,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3686,6 +3663,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3697,6 +3675,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3783,7 +3762,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 101c39553a..3552a8db59 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1531,3 +1531,28 @@ ReplaceVarsFromTargetList(Node *node, (void *) &context, outer_hasSubLinks); } + +/* + * ConcatRTEPermissionInfoLists + * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * + * Also updates perminfoindex of the RTEs in src_rtable to point to the + * "source" perminfos after they have been added into *dest_rtepermlist. + */ +void +ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable) +{ + ListCell *l; + int offset = list_length(*dest_rtepermlist); + + *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + + foreach(l, src_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (rte->perminfoindex > 0) + rte->perminfoindex += offset; + } +} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b2a7237430..e4ce49d606 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RTEPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRTEPermissionInfo(root->rtepermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ab97e71dd7..baf8c542b8 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1598,6 +1599,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo; int clause_relid; Oid userid; @@ -1646,10 +1648,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); /* Table-level SELECT privilege is sufficient for all columns */ if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 1d503e7e01..1d4611fb94 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1376,6 +1376,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; + RTEPermissionInfo *pk_perminfo; + RTEPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1398,27 +1400,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; + + pk_perminfo = makeNode(RTEPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RTEPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fkrte, pkrte), + list_make2(fk_perminfo, pk_perminfo), false)) return false; /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 234fb66580..e1065db5c7 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5139,7 +5139,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5191,7 +5191,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5270,7 +5270,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5318,7 +5318,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5379,6 +5379,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5387,7 +5388,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5456,7 +5457,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 00dc0f2403..7c439a3cfb 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RTEPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index e37c6032ae..4fbb8801ff 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rtepermlist; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ed95ed1176..89b10ee909 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,9 +80,9 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); -extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPermissions_hook_type) (List *, List *, bool); +extern PGDLLIMPORT ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook; /* @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rtepermlist, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..c32834a9e8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rtepermlist; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 633e7671b3..080680ecd0 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -152,6 +152,9 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -965,37 +968,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1051,11 +1023,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1175,14 +1152,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 6bda383bea..99c8d4f611 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrtepermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 21e642a64c..aaff24256e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -72,6 +72,10 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -703,6 +707,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* user to perform the scan as */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..69665aba41 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rtepermlist: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rtepermlist; /* list of RTEPermissionInfo nodes for + * each RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rtepermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rtepermlist entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 484db165db..3cf475513b 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,9 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RTEPermissionInfo *AddRTEPermissionInfo(List **rtepermlist, + RangeTblEntry *rte); +extern RTEPermissionInfo *GetRTEPermissionInfo(List *rtepermlist, + RangeTblEntry *rte); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..05c3680cd6 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index f001ca41bb..0379dd9673 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,5 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); +extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable); #endif /* REWRITEMANIP_H */ diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c index 4b4e259cd2..bfa9263233 100644 --- a/src/test/modules/test_oat_hooks/test_oat_hooks.c +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -46,7 +46,7 @@ static bool REGRESS_suset_variable2 = false; /* Saved hook values */ static object_access_hook_type next_object_access_hook = NULL; static object_access_hook_type_str next_object_access_hook_str = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* Test Object Access Type Hook hooks */ @@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access, int subId, void *arg); static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg); -static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static bool REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort); static void REGRESS_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, @@ -219,8 +219,8 @@ _PG_init(void) object_access_hook_str = REGRESS_object_access_hook_str; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = REGRESS_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; @@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i } static bool -REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort) { bool am_super = superuser_arg(GetUserId()); bool allow = true; @@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) /* Forward to next hook in the chain */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + !(*next_exec_check_perms_hook) (rangeTabls, rtepermlist, do_abort)) allow = false; if (allow) -- 2.35.3
v22-0002-Do-not-add-hidden-OLD-NEW-RTEs-to-stored-view-ru.patch (application/octet-stream, 120.6K) download From 3c0c6b3b16787fc427430fa610225dbf7b982760 Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Fri, 20 Aug 2021 20:05:26 +0900 Subject: [PATCH v22 2/4] Do not add hidden OLD/NEW RTEs to stored view rule actions They were being added so that querying a view relation would correctly check its permissions and lock it during execution, along with the table(s) mentioned in the view query. The commit that introduced RTEPermissionInfo nodes into query processing to handle permission checking makes it redundant to have an RTE for that purpose. Though an RTE still must be present for the view relations mentioned in the query to be locked during execution and for them to be remembered in PlannedStmt.relationOids, so this commit teaches the rewriter to add a copy of the original view RTE. As this changes the shape of the view queries stored in the catalog due to hidden OLD/NEW RTEs no longer being present in the range table, a bunch of regression tests that display those queries now display them such that columns are longer qualified with their relation's name in some cases, like when only one relation is mentioned in the view's query. --- .../postgres_fdw/expected/postgres_fdw.out | 16 +- src/backend/commands/lockcmds.c | 9 - src/backend/commands/view.c | 78 -- src/backend/rewrite/rewriteDefine.c | 7 - src/backend/rewrite/rewriteHandler.c | 33 +- src/bin/pg_dump/t/002_pg_dump.pl | 12 +- src/test/regress/expected/aggregates.out | 26 +- src/test/regress/expected/alter_table.out | 16 +- .../regress/expected/collate.icu.utf8.out | 24 +- .../regress/expected/collate.linux.utf8.out | 24 +- src/test/regress/expected/collate.out | 26 +- src/test/regress/expected/compression.out | 4 +- src/test/regress/expected/create_view.out | 222 +++--- src/test/regress/expected/expressions.out | 24 +- src/test/regress/expected/groupingsets.out | 20 +- src/test/regress/expected/limit.out | 24 +- src/test/regress/expected/matview.out | 24 +- src/test/regress/expected/polymorphism.out | 8 +- src/test/regress/expected/rangefuncs.out | 34 +- src/test/regress/expected/rules.out | 728 +++++++++--------- src/test/regress/expected/tablesample.out | 4 +- src/test/regress/expected/triggers.out | 4 +- src/test/regress/expected/updatable_views.out | 78 +- src/test/regress/expected/window.out | 56 +- src/test/regress/expected/with.out | 32 +- src/test/regress/expected/xml.out | 6 +- src/test/regress/expected/xml_2.out | 6 +- 27 files changed, 729 insertions(+), 816 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index cc9e39c4a5..b6c3749395 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -2606,7 +2606,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1 Foreign Scan Output: ft4.c1, ft5.c2, ft5.c1 Relations: (public.ft4) LEFT JOIN (public.ft5) - Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r6.c1, r7.c2, r7.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r7 ON (((r6.c1 = r7.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r7.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -2669,7 +2669,7 @@ SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c Foreign Scan Output: ft4.c1, t2.c2, t2.c1 Relations: (public.ft4) LEFT JOIN (public.ft5 t2) - Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint + Remote SQL: SELECT r5.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r2 ON (((r5.c1 = r2.c1)))) ORDER BY r5.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST LIMIT 10::bigint OFFSET 10::bigint (4 rows) SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; @@ -6555,10 +6555,10 @@ CREATE VIEW rw_view AS SELECT * FROM foreign_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT foreign_tbl.a, - foreign_tbl.b + SELECT a, + b FROM foreign_tbl - WHERE foreign_tbl.a < foreign_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) @@ -6672,10 +6672,10 @@ CREATE VIEW rw_view AS SELECT * FROM parent_tbl a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT parent_tbl.a, - parent_tbl.b + SELECT a, + b FROM parent_tbl - WHERE parent_tbl.a < parent_tbl.b; + WHERE a < b; Options: check_option=cascaded EXPLAIN (VERBOSE, COSTS OFF) diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index b0747ce291..1d5f30443b 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,15 +194,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 6f07ac2a9c..7e3d5e79bc 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,78 +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; - ParseState *pstate; - - /* - * 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; - nsitem = addRangeTableEntryForRelation(pstate, viewRel, - AccessShareLock, - makeAlias("new", NIL), - false, false); - rt_entry2 = nsitem->p_rte; - - 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. @@ -587,12 +515,6 @@ DefineView(ViewStmt *stmt, const char *queryString, 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. */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 5e9d226e54..ac2568e59a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -786,13 +786,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 7dbbbc629b..156c033bda 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1714,7 +1714,8 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subquery_rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1826,17 +1827,26 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + subquery_rte = rte; - rte->rtekind = RTE_SUBQUERY; - rte->subquery = rule_action; - rte->security_barrier = RelationIsSecurityView(relation); + /* + * Before converting the RTE to become a SUBQUERY, store a copy for the + * executor to be able to lock the view relation and for the planner to be + * able to record the view relation OID in PlannedStmt.relationOids. + */ + rte = copyObject(rte); + parsetree->rtable = lappend(parsetree->rtable, rte); + + subquery_rte->rtekind = RTE_SUBQUERY; + subquery_rte->subquery = rule_action; + subquery_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; - rte->inh = false; /* must not be set for a subquery */ + subquery_rte->relid = InvalidOid; + subquery_rte->relkind = 0; + subquery_rte->rellockmode = 0; + subquery_rte->tablesample = NULL; + subquery_rte->perminfoindex = 0; + subquery_rte->inh = false; /* must not be set for a subquery */ return parsetree; } @@ -1848,9 +1858,6 @@ 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. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index a869321cdf..934a731205 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2225,7 +2225,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E /xm, @@ -2241,7 +2241,7 @@ my %tests = ( SELECT * FROM dump_test.matview;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_second AS\E - \n\s+\QSELECT matview.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview\E \n\s+\QWITH NO DATA;\E /xm, @@ -2257,7 +2257,7 @@ my %tests = ( SELECT * FROM dump_test.matview_second WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_third AS\E - \n\s+\QSELECT matview_second.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_second\E \n\s+\QWITH NO DATA;\E /xm, @@ -2273,7 +2273,7 @@ my %tests = ( SELECT * FROM dump_test.matview_third WITH NO DATA;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_fourth AS\E - \n\s+\QSELECT matview_third.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.matview_third\E \n\s+\QWITH NO DATA;\E /xm, @@ -2291,7 +2291,7 @@ my %tests = ( ALTER COLUMN col2 SET COMPRESSION lz4;', regexp => qr/^ \QCREATE MATERIALIZED VIEW dump_test.matview_compression AS\E - \n\s+\QSELECT test_table.col2\E + \n\s+\QSELECT col2\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH NO DATA;\E .* @@ -3283,7 +3283,7 @@ my %tests = ( SELECT col1 FROM dump_test.test_table;', regexp => qr/^ \QCREATE VIEW dump_test.test_view WITH (security_barrier='true') AS\E - \n\s+\QSELECT test_table.col1\E + \n\s+\QSELECT col1\E \n\s+\QFROM dump_test.test_table\E \n\s+\QWITH LOCAL CHECK OPTION;\E/xm, like => diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index fc2bd40be2..564a7ba1aa 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1623,7 +1623,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c) AS aggfns + + SELECT aggfns(a, b, c) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1675,7 +1675,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY (v.b + 1)) AS aggfns + + SELECT aggfns(a, b, c ORDER BY (b + 1)) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1691,7 +1691,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.a, v.c ORDER BY v.b) AS aggfns + + SELECT aggfns(a, a, c ORDER BY b) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -1707,7 +1707,7 @@ select * from agg_view1; select pg_get_viewdef('agg_view1'::regclass); pg_get_viewdef --------------------------------------------------------------------------------------------------------------------- - SELECT aggfns(v.a, v.b, v.c ORDER BY v.c USING ~<~ NULLS LAST) AS aggfns + + SELECT aggfns(a, b, c ORDER BY c USING ~<~ NULLS LAST) AS aggfns + FROM ( VALUES (1,3,'foo'::text), (0,NULL::integer,NULL::text), (2,2,'bar'::text), (3,1,'baz'::text)) v(a, b, c); (1 row) @@ -2192,15 +2192,15 @@ select ten, from tenk1 group by ten order by ten; select pg_get_viewdef('aggordview1'); - pg_get_viewdef -------------------------------------------------------------------------------------------------------------------------------- - SELECT tenk1.ten, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, + - percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px,+ - rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank + - FROM tenk1 + - GROUP BY tenk1.ten + - ORDER BY tenk1.ten; + pg_get_viewdef +------------------------------------------------------------------------------------------------------------------- + SELECT ten, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) AS p50, + + percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY thousand) FILTER (WHERE (hundred = 1)) AS px,+ + rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY hundred, string4 DESC, hundred) AS rank + + FROM tenk1 + + GROUP BY ten + + ORDER BY ten; (1 row) select * from aggordview1 order by ten; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 346f594ad0..ecf4f65c12 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2479,8 +2479,8 @@ create view at_view_2 as select *, to_json(v1) as j from at_view_1 v1; id | integer | | | | plain | stuff | text | | | | extended | View definition: - SELECT bt.id, - bt.stuff + SELECT id, + stuff FROM at_base_table bt; \d+ at_view_2 @@ -2491,8 +2491,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; @@ -2518,8 +2518,8 @@ create or replace view at_view_1 as select *, 2+2 as more from at_base_table bt; stuff | text | | | | extended | more | integer | | | | plain | View definition: - SELECT bt.id, - bt.stuff, + SELECT id, + stuff, 2 + 2 AS more FROM at_base_table bt; @@ -2531,8 +2531,8 @@ View definition: stuff | text | | | | extended | j | json | | | | extended | View definition: - SELECT v1.id, - v1.stuff, + SELECT id, + stuff, to_json(v1.*) AS j FROM at_view_1 v1; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index d4c8c6de38..4354dc07b8 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -446,18 +446,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index f2d0eb94f2..2098696ec2 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -483,18 +483,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+-------------------------------------------------------------------------- - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower+ + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ | FROM collate_test10; (3 rows) diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index 246832575c..0649564485 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -194,18 +194,18 @@ CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "POSIX") FROM collate_test10; SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------ - collview1 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); - collview2 | SELECT collate_test1.a, + - | collate_test1.b + - | FROM collate_test1 + - | ORDER BY (collate_test1.b COLLATE "C"); - collview3 | SELECT collate_test10.a, + - | lower(((collate_test10.x || collate_test10.x) COLLATE "POSIX")) AS lower+ + table_name | view_definition +------------+------------------------------------------------ + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "POSIX")) AS lower+ | FROM collate_test10; (3 rows) @@ -698,7 +698,7 @@ SELECT c1+1 AS c1p FROM --------+---------+-----------+----------+---------+---------+------------- c1p | integer | | | | plain | View definition: - SELECT ss.c1 + 1 AS c1p + SELECT c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; -- Check conflicting or redundant options in CREATE COLLATION diff --git a/src/test/regress/expected/compression.out b/src/test/regress/expected/compression.out index 4c997e2602..e06ac93a36 100644 --- a/src/test/regress/expected/compression.out +++ b/src/test/regress/expected/compression.out @@ -187,7 +187,7 @@ CREATE MATERIALIZED VIEW compressmv(x) AS SELECT * FROM cmdata1; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; SELECT pg_column_compression(f1) FROM cmdata1; @@ -274,7 +274,7 @@ ALTER MATERIALIZED VIEW compressmv ALTER COLUMN x SET COMPRESSION lz4; --------+------+-----------+----------+---------+----------+-------------+--------------+------------- x | text | | | | extended | lz4 | | View definition: - SELECT cmdata1.f1 AS x + SELECT f1 AS x FROM cmdata1; -- test alter compression method for partitioned tables diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index bf4ff30d86..8f81a3098e 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -395,10 +395,10 @@ CREATE VIEW tt1 AS c | numeric | | | | main | d | character varying(4) | | | | extended | View definition: - SELECT vv.a, - vv.b, - vv.c, - vv.d + SELECT a, + b, + c, + d FROM ( VALUES ('abc'::character varying(3),'0123456789'::character varying,42,'abcd'::character varying(4)), ('0123456789'::character varying,'abc'::character varying(3),42.12,'abc'::character varying(4))) vv(a, b, c, d); SELECT * FROM tt1; @@ -440,9 +440,9 @@ CREATE VIEW aliased_view_4 AS f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -456,9 +456,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM tx1 @@ -472,9 +472,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM tx1 a2 @@ -488,9 +488,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -505,9 +505,9 @@ ALTER TABLE tx1 RENAME TO a1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -521,9 +521,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM tt1 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -537,9 +537,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.f1, - tt1.f2, - tt1.f3 + SELECT f1, + f2, + f3 FROM tt1 WHERE (EXISTS ( SELECT 1 FROM a1 a2 @@ -553,9 +553,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM tt1 tt1_1 @@ -570,9 +570,9 @@ ALTER TABLE tt1 RENAME TO a2; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 @@ -586,9 +586,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM a1 a1_1 @@ -602,9 +602,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM a1 a2_1 @@ -618,9 +618,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -635,9 +635,9 @@ ALTER TABLE a1 RENAME TO tt1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -651,9 +651,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM a2 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -667,9 +667,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a2.f1, - a2.f2, - a2.f3 + SELECT f1, + f2, + f3 FROM a2 WHERE (EXISTS ( SELECT 1 FROM tt1 a2_1 @@ -683,9 +683,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM a2 @@ -701,9 +701,9 @@ ALTER TABLE tx1 SET SCHEMA temp_view_test; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -717,9 +717,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -733,9 +733,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -749,9 +749,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tt1.y1, - tt1.f2, - tt1.f3 + SELECT y1, + f2, + f3 FROM temp_view_test.tt1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 @@ -768,9 +768,9 @@ ALTER TABLE tmp1 RENAME TO tx1; f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -784,9 +784,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT a1.f1, - a1.f2, - a1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 a1 WHERE (EXISTS ( SELECT 1 FROM tt1 @@ -800,9 +800,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.f1, - tx1.f2, - tx1.f3 + SELECT f1, + f2, + f3 FROM temp_view_test.tx1 WHERE (EXISTS ( SELECT 1 FROM tt1 a2 @@ -816,9 +816,9 @@ View definition: f2 | integer | | | | plain | f3 | text | | | | extended | View definition: - SELECT tx1.y1, - tx1.f2, - tx1.f3 + SELECT y1, + f2, + f3 FROM tx1 WHERE (EXISTS ( SELECT 1 FROM temp_view_test.tx1 tx1_1 @@ -1305,10 +1305,10 @@ select pg_get_viewdef('v1', true); select pg_get_viewdef('v4', true); pg_get_viewdef ---------------- - SELECT v1.b, + - v1.c, + - v1.x AS a,+ - v1.ax + + SELECT b, + + c, + + x AS a, + + ax + FROM v1; (1 row) @@ -1585,9 +1585,9 @@ create view tt14v as select t.* from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1623,11 +1623,11 @@ returning pg_describe_object(classid, objid, objsubid) as obj, alter table tt14t drop column f3; -- column f3 is still in the view, sort of ... select pg_get_viewdef('tt14v', true); - pg_get_viewdef ---------------------------------- - SELECT t.f1, + - t."?dropped?column?" AS f3,+ - t.f4 + + pg_get_viewdef +------------------------------- + SELECT f1, + + "?dropped?column?" AS f3,+ + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1675,9 +1675,9 @@ alter table tt14t alter column f4 type integer using f4::integer; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f3, + - t.f4 + + SELECT f1, + + f3, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1697,8 +1697,8 @@ create view tt14v as select t.f1, t.f4 from tt14f() t; select pg_get_viewdef('tt14v', true); pg_get_viewdef -------------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f3, f4); (1 row) @@ -1712,8 +1712,8 @@ alter table tt14t drop column f3; -- ok select pg_get_viewdef('tt14v', true); pg_get_viewdef ---------------------------- - SELECT t.f1, + - t.f4 + + SELECT f1, + + f4 + FROM tt14f() t(f1, f4); (1 row) @@ -1806,8 +1806,8 @@ select * from tt17v; select pg_get_viewdef('tt17v', true); pg_get_viewdef --------------------------------------------- - SELECT i.q1, + - i.q2 + + SELECT q1, + + q2 + FROM int8_tbl i + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); (1 row) @@ -2054,7 +2054,7 @@ select pg_get_viewdef('tt25v', true); WITH cte AS MATERIALIZED ( + SELECT pg_get_keywords() AS k+ ) + - SELECT (cte.k).word AS word + + SELECT (k).word AS word + FROM cte; (1 row) @@ -2106,19 +2106,19 @@ select x + y + z as c1, (x,y) <= ANY (values(1,2),(3,4)) as c11 from (values(1,2,3)) v(x,y,z); select pg_get_viewdef('tt26v', true); - pg_get_viewdef --------------------------------------------------------- - SELECT v.x + v.y + v.z AS c1, + - v.x * v.y + v.z AS c2, + - v.x + v.y * v.z AS c3, + - (v.x + v.y) * v.z AS c4, + - v.x * (v.y + v.z) AS c5, + - v.x + (v.y + v.z) AS c6, + - v.x + (v.y # v.z) AS c7, + - v.x > v.y AND (v.y > v.z OR v.x > v.z) AS c8, + - v.x > v.y OR v.y > v.z AND NOT v.x > v.z AS c9, + - ((v.x, v.y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ - ((v.x, v.y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + + pg_get_viewdef +---------------------------------------------------- + SELECT x + y + z AS c1, + + x * y + z AS c2, + + x + y * z AS c3, + + (x + y) * z AS c4, + + x * (y + z) AS c5, + + x + (y + z) AS c6, + + x + (y # z) AS c7, + + x > y AND (y > z OR x > z) AS c8, + + x > y OR y > z AND NOT x > z AS c9, + + ((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+ + ((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 + FROM ( VALUES (1,2,3)) v(x, y, z); (1 row) diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 5bf39fd9aa..0ab6a71894 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -108,12 +108,12 @@ create view numeric_view as f2164 | numeric(16,4) | | | | main | f2n | numeric | | | | main | View definition: - SELECT numeric_tbl.f1, - numeric_tbl.f1::numeric(16,4) AS f1164, - numeric_tbl.f1::numeric AS f1n, - numeric_tbl.f2, - numeric_tbl.f2::numeric(16,4) AS f2164, - numeric_tbl.f2 AS f2n + SELECT f1, + f1::numeric(16,4) AS f1164, + f1::numeric AS f1n, + f2, + f2::numeric(16,4) AS f2164, + f2 AS f2n FROM numeric_tbl; explain (verbose, costs off) select * from numeric_view; @@ -142,12 +142,12 @@ create view bpchar_view as f214 | character(14) | | | | extended | f2n | bpchar | | | | extended | View definition: - SELECT bpchar_tbl.f1, - bpchar_tbl.f1::character(14) AS f114, - bpchar_tbl.f1::bpchar AS f1n, - bpchar_tbl.f2, - bpchar_tbl.f2::character(14) AS f214, - bpchar_tbl.f2 AS f2n + SELECT f1, + f1::character(14) AS f114, + f1::bpchar AS f1n, + f2, + f2::character(14) AS f214, + f2 AS f2n FROM bpchar_tbl; explain (verbose, costs off) select * from bpchar_view diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index fcad5c4093..8e75bfe92a 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -570,16 +570,16 @@ CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) from gstest2 group by rollup ((a,b,c),(c,d)); NOTICE: view "gstest_view" will be a temporary view select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef -------------------------------------------------------------------------------- - SELECT gstest2.a, + - gstest2.b, + - GROUPING(gstest2.a, gstest2.b) AS "grouping", + - sum(gstest2.c) AS sum, + - count(*) AS count, + - max(gstest2.c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((gstest2.a, gstest2.b, gstest2.c), (gstest2.c, gstest2.d)); + pg_get_viewdef +--------------------------------------- + SELECT a, + + b, + + GROUPING(a, b) AS "grouping", + + sum(c) AS sum, + + count(*) AS count, + + max(c) AS max + + FROM gstest2 + + GROUP BY ROLLUP((a, b, c), (c, d)); (1 row) -- Nested queries with 3 or more levels of nesting diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index 8a98bbea8e..a2cd0f9f5b 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -638,10 +638,10 @@ CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 FETCH FIRST 5 ROWS WITH TIES; @@ -653,10 +653,10 @@ CREATE VIEW limit_thousand_v_2 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand OFFSET 10 LIMIT 5; @@ -671,10 +671,10 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand FETCH FIRST (NULL::integer + 1) ROWS WITH TIES; CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 @@ -685,10 +685,10 @@ CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 ----------+---------+-----------+----------+---------+---------+------------- thousand | integer | | | | plain | View definition: - SELECT onek.thousand + SELECT thousand FROM onek - WHERE onek.thousand < 995 - ORDER BY onek.thousand + WHERE thousand < 995 + ORDER BY thousand LIMIT ALL; -- leave these views diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index c109d97635..87b6e569a5 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -100,10 +100,10 @@ CREATE INDEX mvtest_aa ON mvtest_bb (grandtot); type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvm Materialized view "public.mvtest_tvm" @@ -112,10 +112,10 @@ View definition: type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; \d+ mvtest_tvvm Materialized view "public.mvtest_tvvm" @@ -123,7 +123,7 @@ View definition: ----------+---------+-----------+----------+---------+---------+--------------+------------- grandtot | numeric | | | | main | | View definition: - SELECT mvtest_tvv.grandtot + SELECT grandtot FROM mvtest_tvv; \d+ mvtest_bb @@ -134,7 +134,7 @@ View definition: Indexes: "mvtest_aa" btree (grandtot) View definition: - SELECT mvtest_tvvmv.grandtot + SELECT grandtot FROM mvtest_tvvmv; -- test schema behavior @@ -150,7 +150,7 @@ Indexes: "mvtest_tvmm_expr" UNIQUE, btree ((grandtot > 0::numeric)) "mvtest_tvmm_pred" UNIQUE, btree (grandtot) WHERE grandtot < 0::numeric View definition: - SELECT sum(mvtest_tvm.totamt) AS grandtot + SELECT sum(totamt) AS grandtot FROM mvtest_mvschema.mvtest_tvm; SET search_path = mvtest_mvschema, public; @@ -161,10 +161,10 @@ SET search_path = mvtest_mvschema, public; type | text | | | | extended | | totamt | numeric | | | | main | | View definition: - SELECT mvtest_tv.type, - mvtest_tv.totamt + SELECT type, + totamt FROM mvtest_tv - ORDER BY mvtest_tv.type; + ORDER BY type; -- modify the underlying table data INSERT INTO mvtest_t VALUES (6, 'z', 13); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 1cd558d668..bf08e40ed8 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -1801,10 +1801,10 @@ select * from dfview; c3 | bigint | | | | plain | c4 | bigint | | | | plain | View definition: - SELECT int8_tbl.q1, - int8_tbl.q2, - dfunc(int8_tbl.q1, int8_tbl.q2, flag => int8_tbl.q1 > int8_tbl.q2) AS c3, - dfunc(int8_tbl.q1, flag => int8_tbl.q1 < int8_tbl.q2, b => int8_tbl.q2) AS c4 + SELECT q1, + q2, + dfunc(q1, q2, flag => q1 > q2) AS c3, + dfunc(q1, flag => q1 < q2, b => q2) AS c4 FROM int8_tbl; drop view dfview; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index e2e62db6a2..fbb840e848 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -149,9 +149,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -167,9 +167,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM UNNEST(ARRAY[10, 20], ARRAY['foo'::text, 'bar'::text], ARRAY[1.0]) z(a, b, c); (1 row) @@ -185,9 +185,9 @@ select * from vw_ord; select definition from pg_views where viewname='vw_ord'; definition ---------------------------------------------------------------------------------------------------------------------- - SELECT z.a, + - z.b, + - z.c + + SELECT a, + + b, + + c + FROM ROWS FROM(unnest(ARRAY[10, 20]), unnest(ARRAY['foo'::text, 'bar'::text]), generate_series(1, 2)) z(a, b, c); (1 row) @@ -669,14 +669,14 @@ select * from vw_rngfunc; select pg_get_viewdef('vw_rngfunc'); pg_get_viewdef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - SELECT t1.a, + - t1.b, + - t1.c, + - t1.d, + - t1.e, + - t1.f, + - t1.g, + - t1.n + + SELECT a, + + b, + + c, + + d, + + e, + + f, + + g, + + n + FROM ROWS FROM(getrngfunc9(1), getrngfunc7(1) AS (rngfuncid integer, rngfuncsubid integer, rngfuncname text), getrngfunc1(1)) WITH ORDINALITY t1(a, b, c, d, e, f, g, n); (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 9dd137415e..19ef0a6b80 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1302,56 +1302,56 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); -pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, - pg_get_backend_memory_contexts.ident, - pg_get_backend_memory_contexts.parent, - pg_get_backend_memory_contexts.level, - pg_get_backend_memory_contexts.total_bytes, - pg_get_backend_memory_contexts.total_nblocks, - pg_get_backend_memory_contexts.free_bytes, - pg_get_backend_memory_contexts.free_chunks, - pg_get_backend_memory_contexts.used_bytes +pg_backend_memory_contexts| SELECT name, + ident, + parent, + level, + total_bytes, + total_nblocks, + free_bytes, + free_chunks, + used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); -pg_config| SELECT pg_config.name, - pg_config.setting +pg_config| SELECT name, + setting FROM pg_config() pg_config(name, setting); -pg_cursors| SELECT c.name, - c.statement, - c.is_holdable, - c.is_binary, - c.is_scrollable, - c.creation_time +pg_cursors| SELECT name, + statement, + is_holdable, + is_binary, + is_scrollable, + creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); -pg_file_settings| SELECT a.sourcefile, - a.sourceline, - a.seqno, - a.name, - a.setting, - a.applied, - a.error +pg_file_settings| SELECT sourcefile, + sourceline, + seqno, + name, + setting, + applied, + error FROM pg_show_all_file_settings() a(sourcefile, sourceline, seqno, name, setting, applied, error); -pg_group| SELECT pg_authid.rolname AS groname, - pg_authid.oid AS grosysid, +pg_group| SELECT rolname AS groname, + oid AS grosysid, ARRAY( SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, - a.type, - a.database, - a.user_name, - a.address, - a.netmask, - a.auth_method, - a.options, - a.error + WHERE (NOT rolcanlogin); +pg_hba_file_rules| SELECT line_number, + type, + database, + user_name, + address, + netmask, + auth_method, + options, + error FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, - a.map_name, - a.sys_name, - a.pg_username, - a.error +pg_ident_file_mappings| SELECT line_number, + map_name, + sys_name, + pg_username, + error FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, @@ -1364,22 +1364,22 @@ pg_indexes| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))); -pg_locks| SELECT l.locktype, - l.database, - l.relation, - l.page, - l.tuple, - l.virtualxid, - l.transactionid, - l.classid, - l.objid, - l.objsubid, - l.virtualtransaction, - l.pid, - l.mode, - l.granted, - l.fastpath, - l.waitstart +pg_locks| SELECT locktype, + database, + relation, + page, + tuple, + virtualxid, + transactionid, + classid, + objid, + objsubid, + virtualtransaction, + pid, + mode, + granted, + fastpath, + waitstart FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted, fastpath, waitstart); pg_matviews| SELECT n.nspname AS schemaname, c.relname AS matviewname, @@ -1419,14 +1419,14 @@ pg_policies| SELECT n.nspname AS schemaname, FROM ((pg_policy pol JOIN pg_class c ON ((c.oid = pol.polrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); -pg_prepared_statements| SELECT p.name, - p.statement, - p.prepare_time, - p.parameter_types, - p.result_types, - p.from_sql, - p.generic_plans, - p.custom_plans +pg_prepared_statements| SELECT name, + statement, + prepare_time, + parameter_types, + result_types, + from_sql, + generic_plans, + custom_plans FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, result_types, from_sql, generic_plans, custom_plans); pg_prepared_xacts| SELECT p.transaction, p.gid, @@ -1448,10 +1448,10 @@ pg_publication_tables| SELECT p.pubname, (pg_class c JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.oid = gpt.relid); -pg_replication_origin_status| SELECT pg_show_replication_origin_status.local_id, - pg_show_replication_origin_status.external_id, - pg_show_replication_origin_status.remote_lsn, - pg_show_replication_origin_status.local_lsn +pg_replication_origin_status| SELECT local_id, + external_id, + remote_lsn, + local_lsn FROM pg_show_replication_origin_status() pg_show_replication_origin_status(local_id, external_id, remote_lsn, local_lsn); pg_replication_slots| SELECT l.slot_name, l.plugin, @@ -1697,23 +1697,23 @@ pg_sequences| SELECT n.nspname AS schemaname, JOIN pg_class c ON ((c.oid = s.seqrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); -pg_settings| SELECT a.name, - a.setting, - a.unit, - a.category, - a.short_desc, - a.extra_desc, - a.context, - a.vartype, - a.source, - a.min_val, - a.max_val, - a.enumvals, - a.boot_val, - a.reset_val, - a.sourcefile, - a.sourceline, - a.pending_restart +pg_settings| SELECT name, + setting, + unit, + category, + short_desc, + extra_desc, + context, + vartype, + source, + min_val, + max_val, + enumvals, + boot_val, + reset_val, + sourcefile, + sourceline, + pending_restart FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, @@ -1727,10 +1727,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; -pg_shmem_allocations| SELECT pg_get_shmem_allocations.name, - pg_get_shmem_allocations.off, - pg_get_shmem_allocations.size, - pg_get_shmem_allocations.allocated_size +pg_shmem_allocations| SELECT name, + off, + size, + allocated_size FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size); pg_stat_activity| SELECT s.datid, d.datname, @@ -1798,13 +1798,13 @@ pg_stat_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_archiver| SELECT s.archived_count, - s.last_archived_wal, - s.last_archived_time, - s.failed_count, - s.last_failed_wal, - s.last_failed_time, - s.stats_reset +pg_stat_archiver| SELECT archived_count, + last_archived_wal, + last_archived_time, + failed_count, + last_failed_wal, + last_failed_time, + stats_reset FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset); pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, @@ -1817,57 +1817,57 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; -pg_stat_database| SELECT d.oid AS datid, - d.datname, +pg_stat_database| SELECT oid AS datid, + datname, CASE - WHEN (d.oid = (0)::oid) THEN 0 - ELSE pg_stat_get_db_numbackends(d.oid) + WHEN (oid = (0)::oid) THEN 0 + ELSE pg_stat_get_db_numbackends(oid) END AS numbackends, - pg_stat_get_db_xact_commit(d.oid) AS xact_commit, - pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, - (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, - pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, - pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, - pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, - pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, - pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, - pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(d.oid) AS conflicts, - pg_stat_get_db_temp_files(d.oid) AS temp_files, - pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, - pg_stat_get_db_deadlocks(d.oid) AS deadlocks, - pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures, - pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure, - pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, - pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, - pg_stat_get_db_session_time(d.oid) AS session_time, - pg_stat_get_db_active_time(d.oid) AS active_time, - pg_stat_get_db_idle_in_transaction_time(d.oid) AS idle_in_transaction_time, - pg_stat_get_db_sessions(d.oid) AS sessions, - pg_stat_get_db_sessions_abandoned(d.oid) AS sessions_abandoned, - pg_stat_get_db_sessions_fatal(d.oid) AS sessions_fatal, - pg_stat_get_db_sessions_killed(d.oid) AS sessions_killed, - pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset + pg_stat_get_db_xact_commit(oid) AS xact_commit, + pg_stat_get_db_xact_rollback(oid) AS xact_rollback, + (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS blks_read, + pg_stat_get_db_blocks_hit(oid) AS blks_hit, + pg_stat_get_db_tuples_returned(oid) AS tup_returned, + pg_stat_get_db_tuples_fetched(oid) AS tup_fetched, + pg_stat_get_db_tuples_inserted(oid) AS tup_inserted, + pg_stat_get_db_tuples_updated(oid) AS tup_updated, + pg_stat_get_db_tuples_deleted(oid) AS tup_deleted, + pg_stat_get_db_conflict_all(oid) AS conflicts, + pg_stat_get_db_temp_files(oid) AS temp_files, + pg_stat_get_db_temp_bytes(oid) AS temp_bytes, + pg_stat_get_db_deadlocks(oid) AS deadlocks, + pg_stat_get_db_checksum_failures(oid) AS checksum_failures, + pg_stat_get_db_checksum_last_failure(oid) AS checksum_last_failure, + pg_stat_get_db_blk_read_time(oid) AS blk_read_time, + pg_stat_get_db_blk_write_time(oid) AS blk_write_time, + pg_stat_get_db_session_time(oid) AS session_time, + pg_stat_get_db_active_time(oid) AS active_time, + pg_stat_get_db_idle_in_transaction_time(oid) AS idle_in_transaction_time, + pg_stat_get_db_sessions(oid) AS sessions, + pg_stat_get_db_sessions_abandoned(oid) AS sessions_abandoned, + pg_stat_get_db_sessions_fatal(oid) AS sessions_fatal, + pg_stat_get_db_sessions_killed(oid) AS sessions_killed, + pg_stat_get_db_stat_reset_time(oid) AS stats_reset FROM ( SELECT 0 AS oid, NULL::name AS datname UNION ALL SELECT pg_database.oid, pg_database.datname FROM pg_database) d; -pg_stat_database_conflicts| SELECT d.oid AS datid, - d.datname, - pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, - pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, - pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, - pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, - pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock +pg_stat_database_conflicts| SELECT oid AS datid, + datname, + pg_stat_get_db_conflict_tablespace(oid) AS confl_tablespace, + pg_stat_get_db_conflict_lock(oid) AS confl_lock, + pg_stat_get_db_conflict_snapshot(oid) AS confl_snapshot, + pg_stat_get_db_conflict_bufferpin(oid) AS confl_bufferpin, + pg_stat_get_db_conflict_startup_deadlock(oid) AS confl_deadlock FROM pg_database d; -pg_stat_gssapi| SELECT s.pid, - s.gss_auth AS gss_authenticated, - s.gss_princ AS principal, - s.gss_enc AS encrypted +pg_stat_gssapi| SELECT pid, + gss_auth AS gss_authenticated, + gss_princ AS principal, + gss_enc AS encrypted FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_progress_analyze| SELECT s.pid, s.datid, d.datname, @@ -1890,8 +1890,8 @@ pg_stat_progress_analyze| SELECT s.pid, (s.param8)::oid AS current_child_table_relid FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_progress_basebackup| SELECT s.pid, - CASE s.param1 +pg_stat_progress_basebackup| SELECT pid, + CASE param1 WHEN 0 THEN 'initializing'::text WHEN 1 THEN 'waiting for checkpoint to finish'::text WHEN 2 THEN 'estimating backup size'::text @@ -1900,13 +1900,13 @@ pg_stat_progress_basebackup| SELECT s.pid, WHEN 5 THEN 'transferring wal files'::text ELSE NULL::text END AS phase, - CASE s.param2 + CASE param2 WHEN '-1'::integer THEN NULL::bigint - ELSE s.param2 + ELSE param2 END AS backup_total, - s.param3 AS backup_streamed, - s.param4 AS tablespaces_total, - s.param5 AS tablespaces_streamed + param3 AS backup_streamed, + param4 AS tablespaces_total, + param5 AS tablespaces_streamed FROM pg_stat_get_progress_info('BASEBACKUP'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20); pg_stat_progress_cluster| SELECT s.pid, s.datid, @@ -2016,16 +2016,16 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param7 AS num_dead_tuples FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); -pg_stat_recovery_prefetch| SELECT s.stats_reset, - s.prefetch, - s.hit, - s.skip_init, - s.skip_new, - s.skip_fpw, - s.skip_rep, - s.wal_distance, - s.block_distance, - s.io_depth +pg_stat_recovery_prefetch| SELECT stats_reset, + prefetch, + hit, + skip_init, + skip_new, + skip_fpw, + skip_rep, + wal_distance, + block_distance, + io_depth FROM pg_stat_get_recovery_prefetch() s(stats_reset, prefetch, hit, skip_init, skip_new, skip_fpw, skip_rep, wal_distance, block_distance, io_depth); pg_stat_replication| SELECT s.pid, s.usesysid, @@ -2063,26 +2063,26 @@ pg_stat_replication_slots| SELECT s.slot_name, FROM pg_replication_slots r, LATERAL pg_stat_get_replication_slot((r.slot_name)::text) s(slot_name, spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset) WHERE (r.datoid IS NOT NULL); -pg_stat_slru| SELECT s.name, - s.blks_zeroed, - s.blks_hit, - s.blks_read, - s.blks_written, - s.blks_exists, - s.flushes, - s.truncates, - s.stats_reset +pg_stat_slru| SELECT name, + blks_zeroed, + blks_hit, + blks_read, + blks_written, + blks_exists, + flushes, + truncates, + stats_reset FROM pg_stat_get_slru() s(name, blks_zeroed, blks_hit, blks_read, blks_written, blks_exists, flushes, truncates, stats_reset); -pg_stat_ssl| SELECT s.pid, - s.ssl, - s.sslversion AS version, - s.sslcipher AS cipher, - s.sslbits AS bits, - s.ssl_client_dn AS client_dn, - s.ssl_client_serial AS client_serial, - s.ssl_issuer_dn AS issuer_dn +pg_stat_ssl| SELECT pid, + ssl, + sslversion AS version, + sslcipher AS cipher, + sslbits AS bits, + ssl_client_dn AS client_dn, + ssl_client_serial AS client_serial, + ssl_issuer_dn AS issuer_dn FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id) - WHERE (s.client_port IS NOT NULL); + WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, st.pid, @@ -2101,41 +2101,41 @@ pg_stat_subscription_stats| SELECT ss.subid, ss.stats_reset FROM pg_subscription s, LATERAL pg_stat_get_subscription_stats(s.oid) ss(subid, apply_error_count, sync_error_count, stats_reset); -pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_stat_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2145,68 +2145,68 @@ pg_stat_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); -pg_stat_user_indexes| SELECT pg_stat_all_indexes.relid, - pg_stat_all_indexes.indexrelid, - pg_stat_all_indexes.schemaname, - pg_stat_all_indexes.relname, - pg_stat_all_indexes.indexrelname, - pg_stat_all_indexes.idx_scan, - pg_stat_all_indexes.idx_tup_read, - pg_stat_all_indexes.idx_tup_fetch +pg_stat_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_scan, + idx_tup_read, + idx_tup_fetch FROM pg_stat_all_indexes - WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_stat_user_tables| SELECT pg_stat_all_tables.relid, - pg_stat_all_tables.schemaname, - pg_stat_all_tables.relname, - pg_stat_all_tables.seq_scan, - pg_stat_all_tables.seq_tup_read, - pg_stat_all_tables.idx_scan, - pg_stat_all_tables.idx_tup_fetch, - pg_stat_all_tables.n_tup_ins, - pg_stat_all_tables.n_tup_upd, - pg_stat_all_tables.n_tup_del, - pg_stat_all_tables.n_tup_hot_upd, - pg_stat_all_tables.n_live_tup, - pg_stat_all_tables.n_dead_tup, - pg_stat_all_tables.n_mod_since_analyze, - pg_stat_all_tables.n_ins_since_vacuum, - pg_stat_all_tables.last_vacuum, - pg_stat_all_tables.last_autovacuum, - pg_stat_all_tables.last_analyze, - pg_stat_all_tables.last_autoanalyze, - pg_stat_all_tables.vacuum_count, - pg_stat_all_tables.autovacuum_count, - pg_stat_all_tables.analyze_count, - pg_stat_all_tables.autoanalyze_count + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd, + n_live_tup, + n_dead_tup, + n_mod_since_analyze, + n_ins_since_vacuum, + last_vacuum, + last_autovacuum, + last_analyze, + last_autoanalyze, + vacuum_count, + autovacuum_count, + analyze_count, + autoanalyze_count FROM pg_stat_all_tables - WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); -pg_stat_wal| SELECT w.wal_records, - w.wal_fpi, - w.wal_bytes, - w.wal_buffers_full, - w.wal_write, - w.wal_sync, - w.wal_write_time, - w.wal_sync_time, - w.stats_reset + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_wal| SELECT wal_records, + wal_fpi, + wal_bytes, + wal_buffers_full, + wal_write, + wal_sync, + wal_write_time, + wal_sync_time, + stats_reset FROM pg_stat_get_wal() w(wal_records, wal_fpi, wal_bytes, wal_buffers_full, wal_write, wal_sync, wal_write_time, wal_sync_time, stats_reset); -pg_stat_wal_receiver| SELECT s.pid, - s.status, - s.receive_start_lsn, - s.receive_start_tli, - s.written_lsn, - s.flushed_lsn, - s.received_tli, - s.last_msg_send_time, - s.last_msg_receipt_time, - s.latest_end_lsn, - s.latest_end_time, - s.slot_name, - s.sender_host, - s.sender_port, - s.conninfo +pg_stat_wal_receiver| SELECT pid, + status, + receive_start_lsn, + receive_start_tli, + written_lsn, + flushed_lsn, + received_tli, + last_msg_send_time, + last_msg_receipt_time, + latest_end_lsn, + latest_end_time, + slot_name, + sender_host, + sender_port, + conninfo FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, written_lsn, flushed_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name, sender_host, sender_port, conninfo) - WHERE (s.pid IS NOT NULL); + WHERE (pid IS NOT NULL); pg_stat_xact_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, @@ -2223,19 +2223,19 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char", 'p'::"char"])) GROUP BY c.oid, n.nspname, c.relname; -pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_sys_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, @@ -2245,19 +2245,19 @@ pg_stat_xact_user_functions| SELECT p.oid AS funcid, FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); -pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, - pg_stat_xact_all_tables.schemaname, - pg_stat_xact_all_tables.relname, - pg_stat_xact_all_tables.seq_scan, - pg_stat_xact_all_tables.seq_tup_read, - pg_stat_xact_all_tables.idx_scan, - pg_stat_xact_all_tables.idx_tup_fetch, - pg_stat_xact_all_tables.n_tup_ins, - pg_stat_xact_all_tables.n_tup_upd, - pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd +pg_stat_xact_user_tables| SELECT relid, + schemaname, + relname, + seq_scan, + seq_tup_read, + idx_scan, + idx_tup_fetch, + n_tup_ins, + n_tup_upd, + n_tup_del, + n_tup_hot_upd FROM pg_stat_xact_all_tables - WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, @@ -2301,64 +2301,64 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); -pg_statio_sys_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit +pg_statio_sys_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); -pg_statio_sys_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_sys_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); -pg_statio_user_indexes| SELECT pg_statio_all_indexes.relid, - pg_statio_all_indexes.indexrelid, - pg_statio_all_indexes.schemaname, - pg_statio_all_indexes.relname, - pg_statio_all_indexes.indexrelname, - pg_statio_all_indexes.idx_blks_read, - pg_statio_all_indexes.idx_blks_hit + WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text)); +pg_statio_user_indexes| SELECT relid, + indexrelid, + schemaname, + relname, + indexrelname, + idx_blks_read, + idx_blks_hit FROM pg_statio_all_indexes - WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); -pg_statio_user_sequences| SELECT pg_statio_all_sequences.relid, - pg_statio_all_sequences.schemaname, - pg_statio_all_sequences.relname, - pg_statio_all_sequences.blks_read, - pg_statio_all_sequences.blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_sequences| SELECT relid, + schemaname, + relname, + blks_read, + blks_hit FROM pg_statio_all_sequences - WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); -pg_statio_user_tables| SELECT pg_statio_all_tables.relid, - pg_statio_all_tables.schemaname, - pg_statio_all_tables.relname, - pg_statio_all_tables.heap_blks_read, - pg_statio_all_tables.heap_blks_hit, - pg_statio_all_tables.idx_blks_read, - pg_statio_all_tables.idx_blks_hit, - pg_statio_all_tables.toast_blks_read, - pg_statio_all_tables.toast_blks_hit, - pg_statio_all_tables.tidx_blks_read, - pg_statio_all_tables.tidx_blks_hit + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_statio_user_tables| SELECT relid, + schemaname, + relname, + heap_blks_read, + heap_blks_hit, + idx_blks_read, + idx_blks_hit, + toast_blks_read, + toast_blks_hit, + tidx_blks_read, + tidx_blks_hit FROM pg_statio_all_tables - WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); pg_stats| SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, @@ -2543,24 +2543,24 @@ pg_tables| SELECT n.nspname AS schemaname, LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char"])); -pg_timezone_abbrevs| SELECT pg_timezone_abbrevs.abbrev, - pg_timezone_abbrevs.utc_offset, - pg_timezone_abbrevs.is_dst +pg_timezone_abbrevs| SELECT abbrev, + utc_offset, + is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); -pg_timezone_names| SELECT pg_timezone_names.name, - pg_timezone_names.abbrev, - pg_timezone_names.utc_offset, - pg_timezone_names.is_dst +pg_timezone_names| SELECT name, + abbrev, + utc_offset, + is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); -pg_user| SELECT pg_shadow.usename, - pg_shadow.usesysid, - pg_shadow.usecreatedb, - pg_shadow.usesuper, - pg_shadow.userepl, - pg_shadow.usebypassrls, +pg_user| SELECT usename, + usesysid, + usecreatedb, + usesuper, + userepl, + usebypassrls, '********'::text AS passwd, - pg_shadow.valuntil, - pg_shadow.useconfig + valuntil, + useconfig FROM pg_shadow; pg_user_mappings| SELECT u.oid AS umid, s.oid AS srvid, @@ -3080,7 +3080,7 @@ SELECT * FROM rule_v1; --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rule_t1.a + SELECT a FROM rule_t1; Rules: newinsertrule AS @@ -3119,8 +3119,8 @@ alter table rule_v1 rename column column2 to q2; column1 | integer | | | | plain | q2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1, - "*VALUES*".column2 AS q2 + SELECT column1, + column2 AS q2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3132,8 +3132,8 @@ create view rule_v1(x) as values(1,2); x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT "*VALUES*".column1 AS x, - "*VALUES*".column2 + SELECT column1 AS x, + column2 FROM (VALUES (1,2)) "*VALUES*"; drop view rule_v1; @@ -3145,8 +3145,8 @@ create view rule_v1(x) as select * from (values(1,2)) v; x | integer | | | | plain | column2 | integer | | | | plain | View definition: - SELECT v.column1 AS x, - v.column2 + SELECT column1 AS x, + column2 FROM ( VALUES (1,2)) v; drop view rule_v1; @@ -3158,8 +3158,8 @@ create view rule_v1(x) as select * from (values(1,2)) v(q,w); x | integer | | | | plain | w | integer | | | | plain | View definition: - SELECT v.q AS x, - v.w + SELECT q AS x, + w FROM ( VALUES (1,2)) v(q, w); drop view rule_v1; diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out index 60bb4e8e3e..9ff4611640 100644 --- a/src/test/regress/expected/tablesample.out +++ b/src/test/regress/expected/tablesample.out @@ -74,7 +74,7 @@ CREATE VIEW test_tablesample_v2 AS --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2); \d+ test_tablesample_v2 @@ -83,7 +83,7 @@ View definition: --------+---------+-----------+----------+---------+---------+------------- id | integer | | | | plain | View definition: - SELECT test_tablesample.id + SELECT id FROM test_tablesample TABLESAMPLE system (99); -- check a sampled query doesn't affect cursor in progress diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 8b8eadd181..019c4726f6 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1277,8 +1277,8 @@ DROP TRIGGER instead_of_delete_trig ON main_view; a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT main_table.a, - main_table.b + SELECT a, + b FROM main_table; Triggers: after_del_stmt_trig AFTER DELETE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('after_view_del_stmt') diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index d57eeb761c..b49f091d2f 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1903,19 +1903,19 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WHERE a < b a | integer | | | | plain | b | integer | | | | plain | View definition: - SELECT base_tbl.a, - base_tbl.b + SELECT a, + b FROM base_tbl - WHERE base_tbl.a < base_tbl.b; + WHERE a < b; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view1'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a, +| LOCAL | YES | YES | NO | NO | NO - | | | base_tbl.b +| | | | | | - | | | FROM base_tbl +| | | | | | - | | | WHERE (base_tbl.a < base_tbl.b); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a, +| LOCAL | YES | YES | NO | NO | NO + | | | b +| | | | | | + | | | FROM base_tbl+| | | | | | + | | | WHERE (a < b); | | | | | | (1 row) INSERT INTO rw_view1 VALUES(3,4); -- ok @@ -1956,17 +1956,17 @@ CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=cascaded SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-5); -- should fail @@ -1996,17 +1996,17 @@ CREATE OR REPLACE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a < 10 --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; Options: check_option=local SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| LOCAL | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| LOCAL | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (-10); -- ok, but not in view @@ -2037,16 +2037,16 @@ ALTER VIEW rw_view2 RESET (check_option); --------+---------+-----------+----------+---------+---------+------------- a | integer | | | | plain | View definition: - SELECT rw_view1.a + SELECT a FROM rw_view1 - WHERE rw_view1.a < 10; + WHERE a < 10; SELECT * FROM information_schema.views WHERE table_name = 'rw_view2'; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+----------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a < 10); | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a < 10); | | | | | | (1 row) INSERT INTO rw_view2 VALUES (30); -- ok, but not in view @@ -2068,15 +2068,15 @@ CREATE VIEW rw_view1 AS SELECT * FROM base_tbl WITH CHECK OPTION; CREATE VIEW rw_view2 AS SELECT * FROM rw_view1 WHERE a > 0; CREATE VIEW rw_view3 AS SELECT * FROM rw_view2 WITH CHECK OPTION; SELECT * FROM information_schema.views WHERE table_name LIKE E'rw\\_view_' ORDER BY table_name; - table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into ----------------+--------------+------------+---------------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- - regression | public | rw_view1 | SELECT base_tbl.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM base_tbl; | | | | | | - regression | public | rw_view2 | SELECT rw_view1.a +| NONE | YES | YES | NO | NO | NO - | | | FROM rw_view1 +| | | | | | - | | | WHERE (rw_view1.a > 0); | | | | | | - regression | public | rw_view3 | SELECT rw_view2.a +| CASCADED | YES | YES | NO | NO | NO - | | | FROM rw_view2; | | | | | | + table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into +---------------+--------------+------------+-------------------+--------------+--------------+--------------------+----------------------+----------------------+---------------------------- + regression | public | rw_view1 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM base_tbl; | | | | | | + regression | public | rw_view2 | SELECT a +| NONE | YES | YES | NO | NO | NO + | | | FROM rw_view1 +| | | | | | + | | | WHERE (a > 0); | | | | | | + regression | public | rw_view3 | SELECT a +| CASCADED | YES | YES | NO | NO | NO + | | | FROM rw_view2; | | | | | | (3 rows) INSERT INTO rw_view1 VALUES (-1); -- ok diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 170bea23c2..3d1d26aa39 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -1212,10 +1212,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1238,10 +1238,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1264,10 +1264,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1290,10 +1290,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ----------------------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------------------ + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1316,10 +1316,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------- - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +----------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1341,10 +1341,10 @@ SELECT * FROM v_window; (10 rows) SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ------------------------------------------------------------------------------------------ - SELECT i.i, + - sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ + pg_get_viewdef +------------------------------------------------------------------------------------- + SELECT i, + + sum(i) OVER (ORDER BY i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows+ FROM generate_series(1, 10) i(i); (1 row) @@ -1353,10 +1353,10 @@ CREATE TEMP VIEW v_window AS SELECT i, min(i) over (order by i range between '1 day' preceding and '10 days' following) as min_i FROM generate_series(now(), now()+'100 days'::interval, '1 hour') i; SELECT pg_get_viewdef('v_window'); - pg_get_viewdef ---------------------------------------------------------------------------------------------------------------------------- - SELECT i.i, + - min(i.i) OVER (ORDER BY i.i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ + pg_get_viewdef +----------------------------------------------------------------------------------------------------------------------- + SELECT i, + + min(i) OVER (ORDER BY i RANGE BETWEEN '@ 1 day'::interval PRECEDING AND '@ 10 days'::interval FOLLOWING) AS min_i+ FROM generate_series(now(), (now() + '@ 100 days'::interval), '@ 1 hour'::interval) i(i); (1 row) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7f2e32d8b0..f40197acbf 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -396,9 +396,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass); subdepartment sd + WHERE (d.parent_department = sd.id)+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -419,9 +419,9 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true); subdepartment sd + WHERE d.parent_department = sd.id+ ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + + SELECT id, + + parent_department, + + name + FROM subdepartment; (1 row) @@ -446,7 +446,7 @@ View definition: FROM t t_1 WHERE t_1.n < 100 ) - SELECT sum(t.n) AS sum + SELECT sum(n) AS sum FROM t; -- corner case in which sub-WITH gets initialized first @@ -882,9 +882,9 @@ select pg_get_viewdef('v_search'); search_graph sg + WHERE (g.f = sg.t) + ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1404,9 +1404,9 @@ select pg_get_viewdef('v_cycle1'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle USING path + - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) @@ -1426,9 +1426,9 @@ select pg_get_viewdef('v_cycle2'); search_graph sg + WHERE (g.f = sg.t) + ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT search_graph.f, + - search_graph.t, + - search_graph.label + + SELECT f, + + t, + + label + FROM search_graph; (1 row) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 948b4e702c..cc213523c0 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -603,12 +603,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index 5fd3886b5e..3986fc1706 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -583,12 +583,12 @@ CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; - table_name | view_definition -------------+------------------------------------------------------------------------------------------------------------------- + table_name | view_definition +------------+------------------------------------------------------------------------------------------------------------ xmlview1 | SELECT xmlcomment('test'::text) AS xmlcomment; xmlview2 | SELECT XMLCONCAT('hello'::xml, 'you'::xml) AS "xmlconcat"; xmlview3 | SELECT XMLELEMENT(NAME element, XMLATTRIBUTES(1 AS ":one:", 'deuce' AS two), 'content&') AS "xmlelement"; - xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(emp.name AS name, emp.age AS age, emp.salary AS pay)) AS "xmlelement"+ + xmlview4 | SELECT XMLELEMENT(NAME employee, XMLFOREST(name AS name, age AS age, salary AS pay)) AS "xmlelement" + | FROM emp; xmlview5 | SELECT XMLPARSE(CONTENT '<abc>x</abc>'::text STRIP WHITESPACE) AS "xmlparse"; xmlview6 | SELECT XMLPI(NAME foo, 'bar'::text) AS "xmlpi"; -- 2.35.3
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-12 13:49  Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  2 siblings, 1 reply; 97+ messages in thread

From: Peter Eisentraut @ 2022-10-12 13:49 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; Tom Lane <tgl@sss.pgh.pa.us>; +Cc: David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 06.10.22 15:29, Amit Langote wrote:
> I tried in the attached 0004.  ModifyTable gets a new member
> extraUpdatedColsBitmaps, which is List of Bitmapset "nodes".
> 
> Actually, List of Bitmapsets turned out to be something that doesn't
> just-work with our Node infrastructure, which I found out thanks to
> -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> first-class support for copy/equal/write/read support for Bitmapsets,
> such that writeNode() can write appropriately labeled versions of them
> and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
> didn't actually go ahead and make*all*  Bitmapsets in the plan trees
> to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
> to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
> just use *_NODE_FIELD().

Seeing that on 64-bit platforms we have a 4-byte padding gap in the 
Bitmapset struct, sticking a node tag in there seems pretty sensible. 
So turning Bitmapset into a proper Node and then making the other 
adjustments you describe makes sense to me.

Making a new thread about this might be best.

(I can't currently comment on the rest of the patch set.  So I don't 
know if you'll really end up needing lists of bitmapsets.  But from here 
it looks like turning bitmapsets into nodes might be a worthwhile change 
just by itself.)






^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-10-13 08:14  Amit Langote <amitlangote09@gmail.com>
  parent: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
  0 siblings, 0 replies; 97+ messages in thread

From: Amit Langote @ 2022-10-13 08:14 UTC (permalink / raw)
  To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Wed, Oct 12, 2022 at 10:50 PM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> On 06.10.22 15:29, Amit Langote wrote:
> > I tried in the attached 0004.  ModifyTable gets a new member
> > extraUpdatedColsBitmaps, which is List of Bitmapset "nodes".
> >
> > Actually, List of Bitmapsets turned out to be something that doesn't
> > just-work with our Node infrastructure, which I found out thanks to
> > -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> > first-class support for copy/equal/write/read support for Bitmapsets,
> > such that writeNode() can write appropriately labeled versions of them
> > and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
> > didn't actually go ahead and make*all*  Bitmapsets in the plan trees
> > to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
> > to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
> > just use *_NODE_FIELD().
>
> Seeing that on 64-bit platforms we have a 4-byte padding gap in the
> Bitmapset struct, sticking a node tag in there seems pretty sensible.
> So turning Bitmapset into a proper Node and then making the other
> adjustments you describe makes sense to me.
>
> Making a new thread about this might be best.
>
> (I can't currently comment on the rest of the patch set.  So I don't
> know if you'll really end up needing lists of bitmapsets.  But from here
> it looks like turning bitmapsets into nodes might be a worthwhile change
> just by itself.)

Ok, thanks.  I'll start a new thread about it.

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-11-03 23:46  Ian Lawrence Barwick <barwick@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 97+ messages in thread

From: Ian Lawrence Barwick @ 2022-11-03 23:46 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

2022年10月15日(土) 15:01 Amit Langote <amitlangote09@gmail.com>:
>
> On Fri, Oct 7, 2022 at 4:31 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > On Fri, Oct 7, 2022 at 3:49 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > > On Fri, Oct 7, 2022 at 1:25 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > > > On Fri, Oct 7, 2022 at 10:04 AM Amit Langote <amitlangote09@gmail.com> wrote:
> > > > > On Thu, Oct 6, 2022 at 10:29 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > > > > > Actually, List of Bitmapsets turned out to be something that doesn't
> > > > > > just-work with our Node infrastructure, which I found out thanks to
> > > > > > -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> > > > > > first-class support for copy/equal/write/read support for Bitmapsets,
> > > > > > such that writeNode() can write appropriately labeled versions of them
> > > > > > and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
> > > > > > didn't actually go ahead and make *all* Bitmapsets in the plan trees
> > > > > > to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
> > > > > > to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
> > > > > > just use *_NODE_FIELD().
> > > > >
> > > > > All meson builds on the cfbot machines seem to have failed, maybe
> > > > > because I didn't update src/include/nodes/meson.build to add
> > > > > 'nodes/bitmapset.h' to the `node_support_input_i` collection.  Here's
> > > > > an updated version assuming that's the problem.  (Will set up meson
> > > > > builds on my machine to avoid this in the future.)
> > > >
> > > > And... noticed that a postgres_fdw test failed, because
> > > > _readBitmapset() not having been changed to set NodeTag would
> > > > "corrupt" any Bitmapsets that were created with it set.
> > >
> > > Broke the other cases while fixing the above.  Attaching a new version
> > > again.  In the latest version, I'm setting Bitmapset.type by hand with
> > > an XXX comment nearby saying that it would be nice to change that to
> > > makeNode(Bitmapset), which I know sounds pretty ad-hoc.
> >
> > Sorry, I attached the wrong patches with the last email.  The
> > "correct" v22 attached this time.
>
> Rebased over c037471832.

This entry was marked as "Needs review" in the CommitFest app but cfbot
reports the patch no longer applies.

We've marked it as "Waiting on Author". As CommitFest 2022-11 is
currently underway, this would be an excellent time update the patch.

Once you think the patchset is ready for review again, you (or any
interested party) can  move the patch entry forward by visiting

    https://commitfest.postgresql.org/40/3224/

and changing the status to "Needs review".


Thanks

Ian Barwick





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-11-10 11:58  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Amit Langote <amitlangote09@gmail.com>
  0 siblings, 2 replies; 97+ messages in thread

From: Alvaro Herrera @ 2022-11-10 11:58 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Ian Lawrence Barwick <barwick@gmail.com>; Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Hello

I've been trying to understand what 0001 does.  One thing that strikes
me is that it seems like it'd be easy to have bugs because of modifying
the perminfo list inadequately.  I couldn't find any cross-check that
some perminfo element that we obtain for a rte does actually match the
relation we wanted to check.  Maybe we could add a test in some central
place that perminfo->relid equals rte->relid?

A related point is that concatenating lists doesn't seem to worry about
not processing one element multiple times and ending up with bogus offsets.
(I suppose you still have to let an element be processed multiple times
in case you have nested subqueries?  I wonder how good is the test
coverage for such scenarios.)

Why do callers of add_rte_to_flat_rtable() have to modify the rte's
perminfoindex themselves, instead of having the function do it for them?
That looks strange.  But also it's odd that flatten_unplanned_rtes
concatenates the two lists after all the perminfoindexes have been
modified, rather than doing both things (adding each RTEs perminfo to
the global list and offsetting the index) as we walk the list, in
flatten_rtes_walker.  It looks like these two actions are disconnected
from one another, but unless I misunderstand, in reality the opposite is
true.

I think the API of ConcatRTEPermissionInfoLists is a bit weird.  Why not
have the function return the resulting list instead, just like
list_append?  It is more verbose, but it seems easier to grok.

Two trivial changes attached.  (Maybe 0002 is not correct, if you're
also trying to reference finalrtepermlist; but in that case I think the
original may have been misleading as well.)

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/

Attachments:

0002-Simplify-comment-a-little-bit.patch.txt (text/plain, 904B) download From 47afb0d3e1cf6fdf77b5ace643ece15e9c618006 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Thu, 10 Nov 2022 12:34:01 +0100 Subject: [PATCH 2/2] Simplify comment a little bit --- src/include/nodes/parsenodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 39e9e82bfc..abcf8ee229 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1026,7 +1026,7 @@ typedef struct RangeTblEntry * avoid getting an additional, lesser lock. * * perminfoindex is 1-based index of the RTEPermissionInfo belonging to - * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * this RTE in the containing query's rtepermlist; 0 if permissions * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ -- 2.30.2
0001-fix-typo.patch.txt (text/plain, 931B) download From 682313f8cd4402b4049792e001b4c24ff691a8f1 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Thu, 10 Nov 2022 12:33:44 +0100 Subject: [PATCH 1/2] fix typo --- src/include/nodes/parsenodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 2d648a8fdd..39e9e82bfc 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1187,7 +1187,7 @@ typedef struct RangeTblEntry * reference is represented by setting the bit for InvalidAttrNumber. * * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns the need + * which triggers to fire and in FDWs to know which changed columns they need * to ship off. * * Generated columns that are caused to be updated by an update to a base -- 2.30.2
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-11-10 12:19  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Amit Langote <amitlangote09@gmail.com>
  2 siblings, 1 reply; 97+ messages in thread

From: Alvaro Herrera @ 2022-11-10 12:19 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 2022-Oct-06, Amit Langote wrote:

> Actually, List of Bitmapsets turned out to be something that doesn't
> just-work with our Node infrastructure, which I found out thanks to
> -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> first-class support for copy/equal/write/read support for Bitmapsets,
> such that writeNode() can write appropriately labeled versions of them
> and nodeRead() can read them as Bitmapsets.  That's done in 0003.  I
> didn't actually go ahead and make *all* Bitmapsets in the plan trees
> to be Nodes, but maybe 0003 can be expanded to do that.  We won't need
> to make gen_node_support.pl emit *_BITMAPSET_FIELD() blurbs then; can
> just use *_NODE_FIELD().

Hmm, is this related to what Tom posted as part of his 0004 in
https://postgr.es/m/2901865.1667685211@sss.pgh.pa.us

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-11-11 16:46  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 97+ messages in thread

From: Tom Lane @ 2022-11-11 16:46 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Amit Langote <amitlangote09@gmail.com>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
> On 2022-Oct-06, Amit Langote wrote:
>> Actually, List of Bitmapsets turned out to be something that doesn't
>> just-work with our Node infrastructure, which I found out thanks to
>> -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
>> first-class support for copy/equal/write/read support for Bitmapsets,

> Hmm, is this related to what Tom posted as part of his 0004 in
> https://postgr.es/m/2901865.1667685211@sss.pgh.pa.us

It could be.  For some reason I thought that Amit had withdrawn
his proposal to make Bitmapsets be Nodes.  But if it's still live,
then the data structure I invented in my 0004 could plausibly be
replaced by a List of Bitmapsets.

The code I was using that for would rather have fixed-size arrays
of Bitmapsets than variable-size Lists, mainly because it always
knows ab initio what the max length of the array will be.  But
I don't think that the preference is so strong that it justifies
a private data structure.

The main thing I was wondering about in connection with that
was whether to assume that there could be other future applications
of the logic to perform multi-bitmapset union, intersection,
etc.  If so, then I'd be inclined to choose different naming and
put those functions in or near to bitmapset.c.  It doesn't look
like Amit's code needs anything like that, but maybe somebody
has an idea about other applications?

Anyway, I concur with Peter's upthread comment that making
Bitmapsets be Nodes is probably justifiable all by itself.
The lack of a Node tag in them now is just because in a 32-bit
world it seemed like unnecessary bloat.  But on 64-bit machines
it's free, and we aren't optimizing for 32-bit any more.

I do not like the details of v24-0003 at all though, because
it seems to envision that a "node Bitmapset" is a different
thing from a raw Bitmapset.  That can only lead to bugs ---
why would we not make it the case that every Bitmapset is
properly labeled with the node tag?

Also, although I'm on board with making Bitmapsets be Nodes,
I don't think I'm on board with changing their dump format.
Planner node dumps would get enormously bulkier and less
readable if we changed things like

   :relids (b 1 2)

to

   :relids
      {BITMAPSET
       (b 1 2)
      }

or whatever the output would look like as the patch stands.
So that needs a bit more effort, but it's surely manageable.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-11-14 07:32  Amit Langote <amitlangote09@gmail.com>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 0 replies; 97+ messages in thread

From: Amit Langote @ 2022-11-14 07:32 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Sat, Nov 12, 2022 at 1:46 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
> > On 2022-Oct-06, Amit Langote wrote:
> >> Actually, List of Bitmapsets turned out to be something that doesn't
> >> just-work with our Node infrastructure, which I found out thanks to
> >> -DWRITE_READ_PARSE_PLAN_TREES.  So, I had to go ahead and add
> >> first-class support for copy/equal/write/read support for Bitmapsets,
>
> > Hmm, is this related to what Tom posted as part of his 0004 in
> > https://postgr.es/m/2901865.1667685211@sss.pgh.pa.us
>
> It could be.  For some reason I thought that Amit had withdrawn
> his proposal to make Bitmapsets be Nodes.

I think you are referring to [1] that I had forgotten to link to here.
I did reimplement a data structure in my patch on the "Re: generic
plans and initial pruning" thread to stop using a List of Bitmapsets,
so the Bitmapset as Nodes functionality became unnecessary there,
though I still need it for the proposal here to move
extraUpdatedColumns (patch 0004) into ModifyTable node.

> The code I was using that for would rather have fixed-size arrays
> of Bitmapsets than variable-size Lists, mainly because it always
> knows ab initio what the max length of the array will be.  But
> I don't think that the preference is so strong that it justifies
> a private data structure.
>
> The main thing I was wondering about in connection with that
> was whether to assume that there could be other future applications
> of the logic to perform multi-bitmapset union, intersection,
> etc.  If so, then I'd be inclined to choose different naming and
> put those functions in or near to bitmapset.c.  It doesn't look
> like Amit's code needs anything like that, but maybe somebody
> has an idea about other applications?

Yes, simple storage of multiple Bitmapsets in a List somewhere in a
parse/plan tree sounded like that would have wider enough use to add
proper node support for.   Assuming you mean trying to generalize
VarAttnoSet in your patch 0004 posted at [2], I wonder if you want to
somehow make its indexability by varno / RT index a part of the
interface of the generic code you're thinking for it?  For example,
varattnoset_*_members collection of routines in that patch seem to
assume that the Bitmapsets at a given index in the provided pair of
VarAttnoSets are somehow related -- covering to the same base relation
in this case.  That does not sound very generalizable but maybe that
is not what you are thinking at all.

> Anyway, I concur with Peter's upthread comment that making
> Bitmapsets be Nodes is probably justifiable all by itself.
> The lack of a Node tag in them now is just because in a 32-bit
> world it seemed like unnecessary bloat.  But on 64-bit machines
> it's free, and we aren't optimizing for 32-bit any more.
>
> I do not like the details of v24-0003 at all though, because
> it seems to envision that a "node Bitmapset" is a different
> thing from a raw Bitmapset.  That can only lead to bugs ---
> why would we not make it the case that every Bitmapset is
> properly labeled with the node tag?

Yeah, I just didn't think hard enough to realize that having
bitmapset.c itself set the node tag is essentially free, and it looks
like a better design anyway than the design where callers get to
choose to make the bitmapset they are manipulating a Node or not.

> Also, although I'm on board with making Bitmapsets be Nodes,
> I don't think I'm on board with changing their dump format.
> Planner node dumps would get enormously bulkier and less
> readable if we changed things like
>
>    :relids (b 1 2)
>
> to
>
>    :relids
>       {BITMAPSET
>        (b 1 2)
>       }
>
> or whatever the output would look like as the patch stands.
> So that needs a bit more effort, but it's surely manageable.

Agreed with leaving the dump format unchanged or not bloating it.

Thanks a lot for 5e1f3b9ebf6e5.

--
Thanks, Amit Langote
EDB: http://www.enterprisedb.com

[1] https://www.postgresql.org/message-id/CA+HiwqG8L3DVoZauJi1-eorLnnoM6VcfJCCauQX8=ofi-qMYCQ@mail.gmail...
[2] https://www.postgresql.org/message-id/2901865.1667685211%40sss.pgh.pa.us





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-11-16 11:44  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 0 replies; 97+ messages in thread

From: Alvaro Herrera @ 2022-11-16 11:44 UTC (permalink / raw)
  To: Amit Langote <amitlangote09@gmail.com>; +Cc: Ian Lawrence Barwick <barwick@gmail.com>; Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Zhihong Yu <zyu@yugabyte.com>; Julien Rouhaud <rjuju123@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On 2022-Nov-10, Alvaro Herrera wrote:

> I couldn't find any cross-check that
> some perminfo element that we obtain for a rte does actually match the
> relation we wanted to check.  Maybe we could add a test in some central
> place that perminfo->relid equals rte->relid?

I hadn't looked hard enough.  This is already in GetRTEPermissionInfo().


> A related point is that concatenating lists doesn't seem to worry about
> not processing one element multiple times and ending up with bogus offsets.

> I think the API of ConcatRTEPermissionInfoLists is a bit weird.  Why not
> have the function return the resulting list instead, just like
> list_append?  It is more verbose, but it seems easier to grok.

Another point related to this.  I noticed that everyplace we do
ConcatRTEPermissionInfoLists, it is followed by list_append'ing the RT
list themselves.  This is strange.  Maybe that's the wrong way to look
at this, and instead we should have a function that does both things
together: pass both rtables and rtepermlists and smash them all
together.

I attach your 0001 again with a bunch of other fixups (I don't include
your 0002ff).  I've pushed this to see the CI results, and so far it's
looking good (hasn't finished yet though):
https://cirrus-ci.com/build/5126818977021952

I'll have a look at 0002 now.

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/

Attachments: download all (9 files, .tar.gz)

v25-0001-Rework-query-relation-permission-checking.patch (text/x-diff, 145.9K) download From 5e62fea78ebb7218030188fd7536311f8a08096f Mon Sep 17 00:00:00 2001 From: amitlan <amitlangote09@gmail.com> Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v25 1/9] Rework query relation permission checking Currently, information about the permissions to be checked on relations mentioned in a query is stored in their range table entries. So the executor must scan the entire range table looking for relations that need to have permissions checked. This can make the permission checking part of the executor initialization needlessly expensive when many inheritance children are present in the range range. While the permissions need not be checked on the individual child relations, the executor still must visit every range table entry to filter them out. This commit moves the permission checking information out of the range table entries into a new plan node called RTEPermissionInfo. Every top-level (inheritance "root") RTE_RELATION entry in the range table gets one and a list of those is maintained alongside the range table. The list is initialized by the parser when initializing the range table. The rewriter can add more entries to it as rules/views are expanded. Finally, the planner combines the lists of the individual subqueries into one flat list that is passed down to the executor. To make it quick to find the RTEPermissionInfo entry belonging to a given relation, RangeTblEntry gets a new Index field 'perminfoindex' that stores the correponding RTEPermissionInfo's index in the query's list of the latter. --- contrib/postgres_fdw/postgres_fdw.c | 82 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 12 +- contrib/sepgsql/sepgsql.h | 3 +- src/backend/access/common/attmap.c | 14 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 17 +- src/backend/commands/copyfrom.c | 11 +- src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 6 +- src/backend/executor/execMain.c | 115 +++++------ src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 15 +- src/backend/executor/execUtils.c | 139 +++++++++---- src/backend/nodes/outfuncs.c | 6 +- src/backend/nodes/readfuncs.c | 6 +- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 43 +++- src/backend/optimizer/plan/subselect.c | 7 + src/backend/optimizer/prep/prepjointree.c | 20 +- src/backend/optimizer/util/inherit.c | 167 ++++++++++++---- src/backend/optimizer/util/relnode.c | 21 +- src/backend/parser/analyze.c | 65 +++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_merge.c | 9 +- src/backend/parser/parse_relation.c | 175 +++++++++-------- src/backend/parser/parse_target.c | 18 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 11 +- src/backend/replication/pgoutput/pgoutput.c | 2 +- src/backend/rewrite/rewriteDefine.c | 27 +-- src/backend/rewrite/rewriteHandler.c | 183 ++++++++---------- src/backend/rewrite/rewriteManip.c | 25 +++ src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 19 +- src/backend/utils/adt/selfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 4 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 11 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/parsenodes.h | 95 +++++---- src/include/nodes/pathnodes.h | 3 + src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/parser/parse_node.h | 9 +- src/include/parser/parse_relation.h | 4 + src/include/rewrite/rewriteHandler.h | 1 + src/include/rewrite/rewriteManip.h | 2 + .../modules/test_oat_hooks/test_oat_hooks.c | 12 +- 54 files changed, 966 insertions(+), 566 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 8d7500abfb..a3ec0ecdb4 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -31,6 +31,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -39,6 +40,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -459,7 +461,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -624,7 +627,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -658,12 +660,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermissions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1510,16 +1512,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermissions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1811,7 +1812,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1890,6 +1892,36 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + + /* + * For inheritance child relations, must use the root parent's RTE to + * fetch the permissions entry because that's the only one that actually + * points to any. + */ + if (relInfo->ri_RootResultRelInfo) + rti = relInfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else + rti = relInfo->ri_RangeTableIndex; + + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1901,6 +1933,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1908,6 +1941,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1931,6 +1965,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1942,7 +1977,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2154,6 +2190,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2231,6 +2268,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2247,7 +2286,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2633,7 +2673,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2653,13 +2692,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3962,12 +4000,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3979,12 +4017,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 3bb98dfb06..c4e071b0ea 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -23,6 +23,7 @@ #include "commands/tablecmds.h" #include "executor/executor.h" #include "nodes/bitmapset.h" +#include "parser/parsetree.h" #include "sepgsql.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -277,38 +278,33 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, + bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, rtepermlist) { - RangeTblEntry *rte = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +335,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 363ac06700..6e7f96e7db 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -35,7 +35,7 @@ PG_MODULE_MAGIC; * Saved hook entries (if stacked) */ static object_access_hook_type next_object_access_hook = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* @@ -287,17 +287,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *rangeTbls, List *rtepermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (rangeTbls, rtepermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(rangeTbls, rtepermlist, abort)) return false; return true; @@ -471,8 +471,8 @@ _PG_init(void) object_access_hook = sepgsql_object_access; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = sepgsql_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = sepgsql_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f2a2c795bf..7aa6df92ec 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object, /* * dml.c */ -extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation); +extern bool sepgsql_dml_privileges(List *rangeTabls, List *rtepermlist, + bool abort_on_violation); /* * database.c diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 896f82a22b..1e65d8a120 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,15 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error; AttrMap.attnums[] entry for such an + * outdesc column will be 0 in that case. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +240,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +262,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 4010e20cfb..b2f892d2fd 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index c6ec479004..79ccddce55 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index db4c9dbc23..5ae68842df 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +155,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +177,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index a079c70152..03f8ec459a 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -761,6 +761,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the RTEPermissionInfos into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_rtepermlist = cstate->rtepermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1525,9 +1531,12 @@ BeginCopyFrom(ParseState *pstate, initStringInfo(&cstate->attribute_buf); - /* Assign range table, we'll need it in CopyFrom. */ + /* Assign range table and rtepermlist, we'll need them in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->rtepermlist = pstate->p_rtepermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 91cee27743..b5b860c3ab 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1290,7 +1290,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f006807852..41cf8d5c32 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1206,7 +1206,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9647,7 +9648,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9814,7 +9816,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -10022,7 +10025,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) trigrel = table_open(TriggerRelationId, RowExclusiveLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -10219,7 +10223,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -12335,7 +12340,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -18043,7 +18049,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18981,7 +18988,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index b5a0fc02e5..6f07ac2a9c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) * by 2... * * These extra RT entries are not actually used in the query, - * except for run-time locking and permission checking. + * except for run-time locking. *--------------------------------------------------------------- */ static Query * @@ -411,10 +411,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d78862e660..c43d2215b3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -74,8 +74,8 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ -ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; +/* Hook for plugin to get control in ExecCheckPermissions() */ +ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook = NULL; /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); @@ -90,8 +90,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -554,8 +554,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +565,63 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rtepermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } - if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, - ereport_on_violation); + if (ExecutorCheckPermissions_hook) + result = (*ExecutorCheckPermissions_hook) (rangeTable, rtepermlist, + ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +655,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,15 +687,15 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -713,12 +703,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -773,17 +763,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->rtepermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); - if (rte->rtekind != RTE_RELATION) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) - continue; - - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +802,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->rtepermlist, true); + estate->es_rtepermlist = plannedstmt->rtepermlist; /* * initialize the node's execution state @@ -1858,7 +1846,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1943,7 +1931,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1995,7 +1984,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2102,7 +2092,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 99512826c5..c1c5439fa1 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->rtepermlist = estate->es_rtepermlist; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 40e3c07693..b7b57ec404 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -582,7 +582,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -639,7 +640,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -780,7 +782,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -878,7 +881,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); if (unlikely(!leaf_part_rri->ri_projectNewInfoValid)) ExecInitMergeTupleSlots(mtstate, leaf_part_rri); @@ -1140,7 +1144,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9df1f81ea8..461230b011 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -1251,33 +1252,64 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } -/* Return a bitmap representing columns being inserted */ -Bitmapset * -ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent, something that's allowed with + * traditional inheritance, are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); } + + return resultRelInfo->ri_RootToChildMap; +} + +/* + * GetResultRTEPermissionInfo + * Helper routine for ExecGet*Cols() routines below + * + * The column bitmapsets are stored in RTEPermissionInfos. For inheritance + * child result relations (a partition routing target of an INSERT or a child + * UPDATE target), use the root parent's RTE to fetch the RTEPermissionInfo + * because that's the only one that actually points to any. + */ +static inline RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + else if (relinfo->ri_RangeTableIndex != 0) + rti = relinfo->ri_RangeTableIndex; else { /* @@ -1286,41 +1318,64 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) * firing triggers and the relation is not being inserted into. (See * ExecGetTriggerResultRel.) */ - return NULL; + rti = 0; } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = GetRTEPermissionInfo(estate->es_rtepermlist, rte); + } + + return perminfo; +} + +/* Return a bitmap representing columns being inserted */ +Bitmapset * +ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); + + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); + } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) - { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); - - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; - } - else + if (perminfo == NULL) return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) + { + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); + } + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index f05e72f0dc..59b0fdeb62 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -507,6 +507,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_CHAR_FIELD(relkind); WRITE_INT_FIELD(rellockmode); WRITE_NODE_FIELD(tablesample); + WRITE_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); @@ -560,11 +561,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 23776367c5..966b75f5a6 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -473,6 +473,7 @@ _readRangeTblEntry(void) READ_CHAR_FIELD(relkind); READ_INT_FIELD(rellockmode); READ_NODE_FIELD(tablesample); + READ_UINT_FIELD(perminfoindex); break; case RTE_SUBQUERY: READ_NODE_FIELD(subquery); @@ -536,11 +537,6 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(insertedCols); - READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); READ_NODE_FIELD(securityQuals); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ac86ce9003..5013ac3377 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4148,6 +4148,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5794,7 +5797,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 493a3af0fa..5cddc9d6cf 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrtepermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrtepermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -520,6 +523,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->rtepermlist = glob->finalrtepermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6265,6 +6269,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6392,6 +6397,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRTEPermissionInfo(&query->rtepermlist, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..a26aa36048 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -355,6 +355,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rtepermlist. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -370,14 +373,29 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * flattened rangetable match up with their original indexes. When * recursing, we only care about extracting relation RTEs. */ + rti = 1; foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) + { + /* + * Update perminfoindex, if any, to reflect the correponding + * RTEPermissionInfo's position in the flattened list. + */ + if (rte->perminfoindex > 0) + rte->perminfoindex += list_length(glob->finalrtepermlist); + add_rte_to_flat_rtable(glob, rte); + } + + rti++; } + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + root->parse->rtepermlist); + /* * If there are any dead subqueries, they are not referenced in the Plan * tree, so we must add RTEs contained in them to the flattened rtable @@ -450,6 +468,15 @@ flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) flatten_rtes_walker, (void *) glob, QTW_EXAMINE_RTES_BEFORE); + + /* + * Now add the subquery's RTEPermissionInfos too. flatten_rtes_walker() + * should already have updated the perminfoindex in the RTEs in the + * subquery to reflect the corresponding RTEPermissionInfos' position in + * finalrtepermlist. + */ + glob->finalrtepermlist = list_concat(glob->finalrtepermlist, + rte->subquery->rtepermlist); } static bool @@ -463,7 +490,15 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) + { + /* + * The correponding RTEPermissionInfo will get added to + * finalrtepermlist, so adjust perminfoindex accordingly. + */ + Assert(rte->perminfoindex > 0); + rte->perminfoindex += list_length(glob->finalrtepermlist); add_rte_to_flat_rtable(glob, rte); + } return false; } if (IsA(node, Query)) @@ -483,10 +518,10 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo for executor-startup + * permission checking. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..a61082d27c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,6 +1496,13 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, + subselect->rtable); + /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index b4b9099eb6..b2ab2c14a4 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -175,13 +175,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1204,6 +1197,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, + subquery->rtable); + /* * Now append the adjusted rtable entries to upper query. (We hold off * until after fixing the upper rtable entries; no point in running that @@ -1344,6 +1344,12 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } } + /* + * Add subquery's RTEPermissionInfos into the upper query. This also + * updates the subquery's RTEs' perminfoindex. + */ + ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, + subquery->rtepermlist, rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 3d270e91d6..03e04094e0 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *root_perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + root_perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +312,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +332,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains the root partrel's + * updatedCols translated to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +409,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +468,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, - * relkind, and inh for the child. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. Other - * infrastructure of the parent RTE has to be translated to match the - * child table's column ordering, which we do below, so a "flat" copy is - * sufficient to start with. + * relkind, and inh for the child. Set the child's securityQuals to + * empty, because we only want to apply the parent's RLS conditions + * regardless of what RLS properties individual children may have. + * (This is an intentional choice to make inherited RLS work like regular + * permissions checks.) The parent securityQuals will be propagated to + * children along with other base restriction clauses, so we don't need + * to do it here. Other infrastructure of the parent RTE has to be + * translated to match the child table's column ordering, which we do + * below, so a "flat" copy is sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); @@ -476,7 +491,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,33 +553,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But if - * this is the parent table, we can just duplicate the parent's bitmaps. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ + /* Translate the bitmapset of generated columns being updated. */ if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); - } else - { - childrte->selectedCols = bms_copy(parentrte->selectedCols); - childrte->insertedCols = bms_copy(parentrte->insertedCols); - childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); - } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which @@ -866,3 +859,95 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_multilevel + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel, + RelOptInfo *top_parent_rel, + Bitmapset *top_parent_cols) +{ + Bitmapset *result; + AppendRelInfo *appinfo; + + if (top_parent_cols == NULL) + return NULL; + + /* Recurse if immediate parent is not the top parent. */ + if (rel->parent != top_parent_rel) + { + if (rel->parent) + result = translate_col_privs_multilevel(root, rel->parent, + top_parent_rel, + top_parent_cols); + else + elog(ERROR, "rel with relid %u is not a child rel", rel->relid); + } + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[rel->relid]; + Assert(appinfo != NULL); + + result = translate_col_privs(top_parent_cols, appinfo->translated_vars); + + return result; +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + Index use_relid; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + Assert(root->parse->commandType == CMD_UPDATE); + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * We need to get the updatedCols bitmapset from the relation's + * RTEPermissionInfo. + * + * If it's a simple "base" rel, can just fetch its RTEPermissionInfo that + * must always be present; this cannot get called on non-RELATION RTEs. + * + * For "other" rels, must look up the root parent relation mentioned in the + * query, because only that one gets assigned a RTEPermissionInfo, and + * translate the columns found therein to match the given relation. + */ + use_relid = rel->top_parent_relids == NULL ? rel->relid : + bms_singleton_member(rel->top_parent_relids); + Assert(use_relid == root->parse->resultRelation); + rte = planner_rt_fetch(use_relid, root); + Assert(rte->perminfoindex > 0); + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + if (use_relid != rel->relid) + { + RelOptInfo *top_parent_rel = find_base_rel(root, use_relid); + + updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + perminfo->updatedCols); + extraUpdatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + rte->extraUpdatedCols); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = rte->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index d7b4434e7f..4691f7f344 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -223,7 +224,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though + * only the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(root->parse->rtepermlist, rte); + + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6688c2a865..25324d9486 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; + List *sub_rtepermlist; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -594,10 +596,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing - * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in - * that case we want the rule's OLD and NEW rtable entries to appear as - * part of the SELECT's rtable, not as outer references for it. (Kluge!) + * INSERT/SELECT, arrange to pass the rangetable/rtepermlist/namespace down + * to the SELECT. This can only happen if we are inside a CREATE RULE, + * and in that case we want the rule's OLD and NEW rtable entries to appear + * as part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ @@ -605,6 +607,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; + sub_rtepermlist = pstate->p_rtepermlist; + pstate->p_rtepermlist = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } @@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_rtepermlist = sub_rtepermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -894,7 +899,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -910,8 +915,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1096,8 +1102,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2423,7 +2432,7 @@ List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2435,7 +2444,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2476,8 +2485,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3242,9 +3252,16 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3324,9 +3341,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RTEPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index e01c0734d1..856839f379 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -225,7 +225,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3226,16 +3226,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RTEPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index 7913523b1c..79171fb725 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -214,6 +214,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ qry->targetList = NIL; qry->rtable = pstate->p_rtable; + qry->rtepermlist = pstate->p_rtepermlist; /* * Transform the join condition. This includes references to the target @@ -286,7 +287,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { List *exprList = NIL; ListCell *lc; - RangeTblEntry *rte; + RTEPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; List *icolumns; @@ -345,7 +346,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * of expressions. Also, mark all the target columns as * needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); @@ -359,8 +360,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) false); action->targetList = lappend(action->targetList, tle); - rte->insertedCols = - bms_add_member(rte->insertedCols, + perminfo->insertedCols = + bms_add_member(perminfo->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 81f9ae2f02..4dc8d7ecf5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,10 +1021,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo = + GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1235,10 +1238,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RTEPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1274,6 +1280,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1417,6 +1424,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1453,7 +1461,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1462,12 +1470,8 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1481,7 +1485,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1518,6 +1522,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RTEPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1541,7 +1546,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1550,12 +1555,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRTEPermissionInfo(&pstate->p_rtepermlist, rte); + perminfo->requiredPerms = ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1569,7 +1570,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1643,21 +1644,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1974,20 +1969,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1999,7 +1987,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2066,20 +2054,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2154,19 +2135,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2251,19 +2226,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2278,6 +2247,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2401,19 +2371,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2527,16 +2491,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRTEPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2548,7 +2509,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3173,6 +3134,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, bool require_col_privs, int location) { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3190,7 +3152,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3742,3 +3707,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRTEPermissionInfo + * Creates RTEPermissionInfo for a given RTE and adds it into the + * provided list + * + * Returns the RTEPermissionInfo and sets rte->perminfoindex. + */ +RTEPermissionInfo * +AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->rtekind == RTE_RELATION); + Assert(rte->perminfoindex == 0); + + /* Nope, so make one and add to the list. */ + perminfo = makeNode(RTEPermissionInfo); + perminfo->relid = rte->relid; + perminfo->inh = rte->inh; + /* Other information is set by fetching the node as and where needed. */ + + *rtepermlist = lappend(*rtepermlist, perminfo); + + /* Note its index. */ + rte->perminfoindex = list_length(*rtepermlist); + + return perminfo; +} + +/* + * GetRTEPermissionInfo + * Find RTEPermissionInfo for a given relation in the provided list + * + * This is a simple list_nth() operation though it's good to have the function + * for the various sanity checks. + */ +RTEPermissionInfo * +GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) +{ + RTEPermissionInfo *perminfo; + + Assert(rte->perminfoindex > 0); + if (rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + rte->perminfoindex, rte->relid); + perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, + rte->perminfoindex - 1); + Assert(perminfo != NULL); + if (perminfo->relid != rte->relid) + elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", + rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bd8057bc3e..0415fcec7e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1132,7 +1132,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1367,6 +1367,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RTEPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1381,7 +1382,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1414,11 +1418,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * with its RTEPermissionInfo's selectedCols bitmap showing the whole row + * as needing select permission, as well as the individual columns. + * However, we can only get here for weird notations like (table.*).*, so + * it's not worth trying to clean up --- arguably, the permissions marking + * is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 487eb2041b..efff8c03b3 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1232,7 +1232,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3022,9 +3023,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3080,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->rtepermlist = pstate->p_rtepermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3122,8 +3121,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index e48a3f589a..568344cc23 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -156,6 +156,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -516,6 +517,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRTEPermissionInfo(&estate->es_rtepermlist, rte); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1813,6 +1816,7 @@ apply_handle_update(StringInfo s) bool has_oldtup; TupleTableSlot *remoteslot; RangeTblEntry *target_rte; + RTEPermissionInfo *target_perminfo; MemoryContext oldctx; /* @@ -1861,6 +1865,7 @@ apply_handle_update(StringInfo s) * on the subscriber, since we are not touching those. */ target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_rtepermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1870,14 +1875,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_rte, target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 2ecaa5b907..f2128190d8 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1125,7 +1125,7 @@ init_tuple_slot(PGOutputData *data, Relation relation, /* Map must live as long as the session does. */ oldctx = MemoryContextSwitchTo(CacheMemoryContext); - entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc); + entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false); MemoryContextSwitchTo(oldctx); RelationClose(ancestor); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index db45d8a08b..5f50c87b01 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -797,14 +797,14 @@ 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 rtable entries. + * 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 will be overridden when the view rule is expanded, and the - * checkAsUser field of the NEW entry is irrelevant because that entry's - * 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. + * 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) @@ -831,18 +831,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + /* Set in all RTEPermissionInfos for this query. */ + foreach(l, qry->rtepermlist) + { + RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fb0c687bd8..fda0eacf79 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -353,6 +353,7 @@ rewriteRuleAction(Query *parsetree, Query *sub_action; Query **sub_action_ptr; acquireLocksOnSubLinks_context context; + List *query_rtable; context.for_execute = true; @@ -395,32 +396,35 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. + * Also merge RTEPermissionInfo lists to ensure that all permissions are + * checked correctly. * * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. - * - * NOTE: because planner will destructively alter rtable, we must ensure - * that rule action's rtable is separate and shares no substructure with - * the main rtable. Hence do a deep copy here. + * NOTE: because planner will destructively alter rtable and rtepermlist, + * we must ensure that rule action's lists are separate and shares no + * substructure with the main query's lists. Hence do a deep copy here + * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), + * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtable = list_concat(copyObject(parsetree->rtable), - sub_action->rtable); + sub_action->rtepermlist = copyObject(sub_action->rtepermlist); + query_rtable = copyObject(parsetree->rtable); + ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, + parsetree->rtepermlist, query_rtable); + sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* * There could have been some SubLinks in parsetree's rtable, in which @@ -1624,10 +1628,13 @@ rewriteValuesRTEToNulls(Query *parsetree, RangeTblEntry *rte) /* * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; @@ -1650,7 +1657,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); @@ -1741,8 +1748,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1783,18 +1789,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1858,12 +1852,6 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. - * - * Note: this must happen after markQueryForLocking. That way, any UPDATE - * permission bits needed for sub-views are initially applied to their - * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their - * OLD rangetable entries by the action below (in a recursive call of this - * routine). */ rule_action = fireRIRrules(rule_action, activeRIRs); @@ -1881,28 +1869,9 @@ ApplyRetrieveRule(Query *parsetree, rte->relkind = 0; rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1931,8 +1900,12 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RTEPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3073,6 +3046,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RTEPermissionInfo *view_perminfo; + RTEPermissionInfo *base_perminfo; + RTEPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3209,6 +3185,7 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); Assert(base_rte->rtekind == RTE_RELATION); + base_perminfo = GetRTEPermissionInfo(viewquery->rtepermlist, base_rte); /* * Up to now, the base relation hasn't been touched at all in our query. @@ -3280,57 +3257,59 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * If the view has "security_invoker" set, mark the new target RTE for the - * permissions checks that we want to enforce against the query caller. - * Otherwise we want to enforce them against the view owner. + * If the view has "security_invoker" set, mark the new target relation + * for the permissions checks that we want to enforce against the query + * caller. Otherwise we want to enforce them against the view owner. * * At the relation level, require the same INSERT/UPDATE/DELETE * permissions that the query caller needs against the view. We drop the - * ACL_SELECT bit that is presumably in new_rte->requiredPerms initially. + * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms + * initially. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RTEPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ + view_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, view_rte); + new_rte->perminfoindex = 0; + new_perminfo = AddRTEPermissionInfo(&parsetree->rtepermlist, new_rte); if (RelationHasSecurityInvoker(view)) - new_rte->checkAsUser = InvalidOid; + new_perminfo->checkAsUser = InvalidOid; else - new_rte->checkAsUser = view->rd_rel->relowner; - - new_rte->requiredPerms = view_rte->requiredPerms; + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3434,7 +3413,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3445,8 +3424,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3720,6 +3697,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RTEPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3731,6 +3709,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRTEPermissionInfo(parsetree->rtepermlist, rt_entry); /* * We can use NoLock here since either the parser or @@ -3817,7 +3796,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation); } else if (event == CMD_MERGE) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 101c39553a..3552a8db59 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1531,3 +1531,28 @@ ReplaceVarsFromTargetList(Node *node, (void *) &context, outer_hasSubLinks); } + +/* + * ConcatRTEPermissionInfoLists + * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * + * Also updates perminfoindex of the RTEs in src_rtable to point to the + * "source" perminfos after they have been added into *dest_rtepermlist. + */ +void +ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable) +{ + ListCell *l; + int offset = list_length(*dest_rtepermlist); + + *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + + foreach(l, src_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (rte->perminfoindex > 0) + rte->perminfoindex += offset; + } +} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b2a7237430..e4ce49d606 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RTEPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRTEPermissionInfo(root->rtepermlist, rte); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -243,7 +249,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE || commandType == CMD_MERGE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -286,7 +292,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -342,7 +348,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -371,7 +377,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -474,8 +480,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ab97e71dd7..baf8c542b8 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -32,6 +32,7 @@ #include "optimizer/clauses.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1598,6 +1599,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo; int clause_relid; Oid userid; @@ -1646,10 +1648,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); /* Table-level SELECT privilege is sufficient for all columns */ if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index dc07157037..29f29d664b 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1375,6 +1375,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; + RTEPermissionInfo *pk_perminfo; + RTEPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1397,27 +1399,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; + + pk_perminfo = makeNode(RTEPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RTEPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fkrte, pkrte), + list_make2(fk_perminfo, pk_perminfo), false)) return false; /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d597b7e81f..b7f1d87715 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5142,7 +5142,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5194,7 +5194,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5277,7 +5277,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5325,7 +5325,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5386,6 +5386,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5394,7 +5395,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5463,7 +5464,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index bd6cd4e47b..787ad8b232 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation) /* * 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. + * all RTEPermissionInfos. 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 * it is stored, is that otherwise ALTER TABLE OWNER would have to diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 3ae40cade7..dc0277384f 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 8d9cc5accd..3d2de0ae1b 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -97,7 +97,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rtepermlist; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ed95ed1176..89b10ee909 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,9 +80,9 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); -extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPermissions_hook_type) (List *, List *, bool); +extern PGDLLIMPORT ExecutorCheckPermissions_hook_type ExecutorCheckPermissions_hook; /* @@ -196,7 +196,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rtepermlist, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -600,6 +601,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 01b1727fc0..c32834a9e8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -563,6 +563,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; @@ -610,6 +618,7 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rtepermlist; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 7caff62af7..2d648a8fdd 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -152,6 +152,9 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -966,37 +969,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1052,11 +1024,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1176,14 +1153,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns the need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index a544b313d3..1ef2f89782 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrtepermlist; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 5c2ab1b379..e3a5233dd7 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -75,6 +75,10 @@ typedef struct PlannedStmt /* rtable indexes of target relations for INSERT/UPDATE/DELETE/MERGE */ List *resultRelations; /* integer list of RT indexes, or NIL */ + List *rtepermlist; /* list of RTEPermissionInfo nodes for + * the rtable entries having + * perminfoindex > 0 */ + List *appendRelations; /* list of AppendRelInfo nodes */ List *subplans; /* Plan trees for SubPlan expressions; note @@ -703,6 +707,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* user to perform the scan as */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d7372..9a4f86920c 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de..69665aba41 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rtepermlist: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rtepermlist; /* list of RTEPermissionInfo nodes for + * each RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rtepermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rtepermlist entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 484db165db..3cf475513b 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -120,5 +120,9 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RTEPermissionInfo *AddRTEPermissionInfo(List **rtepermlist, + RangeTblEntry *rte); +extern RTEPermissionInfo *GetRTEPermissionInfo(List *rtepermlist, + RangeTblEntry *rte); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109af..05c3680cd6 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index f001ca41bb..0379dd9673 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,5 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); +extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, + List *src_rtable); #endif /* REWRITEMANIP_H */ diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c index 4b4e259cd2..bfa9263233 100644 --- a/src/test/modules/test_oat_hooks/test_oat_hooks.c +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -46,7 +46,7 @@ static bool REGRESS_suset_variable2 = false; /* Saved hook values */ static object_access_hook_type next_object_access_hook = NULL; static object_access_hook_type_str next_object_access_hook_str = NULL; -static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ExecutorCheckPermissions_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; /* Test Object Access Type Hook hooks */ @@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access, int subId, void *arg); static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg); -static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static bool REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort); static void REGRESS_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, @@ -219,8 +219,8 @@ _PG_init(void) object_access_hook_str = REGRESS_object_access_hook_str; /* DML permission check */ - next_exec_check_perms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + next_exec_check_perms_hook = ExecutorCheckPermissions_hook; + ExecutorCheckPermissions_hook = REGRESS_exec_check_perms; /* ProcessUtility hook */ next_ProcessUtility_hook = ProcessUtility_hook; @@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i } static bool -REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +REGRESS_exec_check_perms(List *rangeTabls, List *rtepermlist, bool do_abort) { bool am_super = superuser_arg(GetUserId()); bool allow = true; @@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) /* Forward to next hook in the chain */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + !(*next_exec_check_perms_hook) (rangeTabls, rtepermlist, do_abort)) allow = false; if (allow) -- 2.30.2
v25-0002-fix-typo.patch (text/x-diff, 935B) download From 51b0d097ef7db85f90a101e62741b76ef02c692b Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Thu, 10 Nov 2022 12:33:44 +0100 Subject: [PATCH v25 2/9] fix typo --- src/include/nodes/parsenodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 2d648a8fdd..39e9e82bfc 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1187,7 +1187,7 @@ typedef struct RangeTblEntry * reference is represented by setting the bit for InvalidAttrNumber. * * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns the need + * which triggers to fire and in FDWs to know which changed columns they need * to ship off. * * Generated columns that are caused to be updated by an update to a base -- 2.30.2
v25-0003-Simplify-comment-a-little-bit.patch (text/x-diff, 908B) download From 8df96aafdc2c0fb7985e364a7c102ce668d155f0 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Thu, 10 Nov 2022 12:34:01 +0100 Subject: [PATCH v25 3/9] Simplify comment a little bit --- src/include/nodes/parsenodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 39e9e82bfc..abcf8ee229 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1026,7 +1026,7 @@ typedef struct RangeTblEntry * avoid getting an additional, lesser lock. * * perminfoindex is 1-based index of the RTEPermissionInfo belonging to - * this RTE in the query's list of RTEPermissionInfos; 0 if permissions + * this RTE in the containing query's rtepermlist; 0 if permissions * need not be checked for the RTE. */ Oid relid; /* OID of the relation */ -- 2.30.2
v25-0004-change-ConcatRTEPermissionInfoLists-API.patch (text/x-diff, 3.4K) download From 061339b832bab44da183f74379fa0ae2f0ff55ca Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Tue, 15 Nov 2022 19:22:45 +0100 Subject: [PATCH v25 4/9] change ConcatRTEPermissionInfoLists API --- src/backend/optimizer/plan/subselect.c | 5 +++-- src/backend/optimizer/prep/prepjointree.c | 10 ++++++---- src/backend/rewrite/rewriteHandler.c | 7 ++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index a61082d27c..cbeb0191fb 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1500,8 +1500,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * Add subquery's RTEPermissionInfos into the upper query. This also * updates the subquery's RTEs' perminfoindex. */ - ConcatRTEPermissionInfoLists(&parse->rtepermlist, subselect->rtepermlist, - subselect->rtable); + parse->rtepermlist = ConcatRTEPermissionInfoLists(parse->rtepermlist, + subselect->rtepermlist, + subselect->rtable); /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index b2ab2c14a4..8d7b243b21 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1201,8 +1201,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, * Add subquery's RTEPermissionInfos into the upper query. This also * updates the subquery's RTEs' perminfoindex. */ - ConcatRTEPermissionInfoLists(&parse->rtepermlist, subquery->rtepermlist, - subquery->rtable); + parse->rtepermlist = ConcatRTEPermissionInfoLists(parse->rtepermlist, + subquery->rtepermlist, + subquery->rtable); /* * Now append the adjusted rtable entries to upper query. (We hold off @@ -1348,8 +1349,9 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) * Add subquery's RTEPermissionInfos into the upper query. This also * updates the subquery's RTEs' perminfoindex. */ - ConcatRTEPermissionInfoLists(&root->parse->rtepermlist, - subquery->rtepermlist, rtable); + root->parse->rtepermlist = ConcatRTEPermissionInfoLists(root->parse->rtepermlist, + subquery->rtepermlist, + rtable); /* * Append child RTEs to parent rtable. */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index fda0eacf79..656a27ea22 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -420,10 +420,11 @@ rewriteRuleAction(Query *parsetree, * for both. Copy rtable before calling ConcatRTEPermissionInfoLists(), * because perminfoindex of those RTEs will be updated there. */ - sub_action->rtepermlist = copyObject(sub_action->rtepermlist); query_rtable = copyObject(parsetree->rtable); - ConcatRTEPermissionInfoLists(&sub_action->rtepermlist, - parsetree->rtepermlist, query_rtable); + sub_action->rtepermlist = + ConcatRTEPermissionInfoLists(copyObject(sub_action->rtepermlist), + parsetree->rtepermlist, + query_rtable); sub_action->rtable = list_concat(query_rtable, sub_action->rtable); /* -- 2.30.2
v25-0005-lfirst-lfirst_node.patch (text/x-diff, 3.6K) download From b3125b9731c3874ced1df15a316a4ce5d68c4aff Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Wed, 16 Nov 2022 12:25:21 +0100 Subject: [PATCH v25 5/9] lfirst -> lfirst_node --- contrib/sepgsql/dml.c | 2 +- src/backend/executor/execMain.c | 4 ++-- src/backend/parser/parse_relation.c | 5 ++--- src/backend/rewrite/rewriteDefine.c | 2 +- src/backend/rewrite/rewriteManip.c | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index c4e071b0ea..3509358fbe 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -285,7 +285,7 @@ sepgsql_dml_privileges(List *rangeTbls, List *rtepermlist, foreach(lr, rtepermlist) { - RTEPermissionInfo *perminfo = lfirst(lr); + RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, lr); uint32 required = 0; List *tableIds; ListCell *li; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index c43d2215b3..926820f19b 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -578,7 +578,7 @@ ExecCheckPermissions(List *rangeTable, List *rtepermlist, foreach(l, rtepermlist) { - RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l); Assert(OidIsValid(perminfo->relid)); result = ExecCheckOneRelPerms(perminfo); @@ -765,7 +765,7 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) */ foreach(l, plannedstmt->rtepermlist) { - RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l); if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 4dc8d7ecf5..5fd01965a4 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -3753,9 +3753,8 @@ GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) if (rte->perminfoindex > list_length(rtepermlist)) elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", rte->perminfoindex, rte->relid); - perminfo = (RTEPermissionInfo *) list_nth(rtepermlist, - rte->perminfoindex - 1); - Assert(perminfo != NULL); + perminfo = list_nth_node(RTEPermissionInfo, rtepermlist, + rte->perminfoindex - 1); if (perminfo->relid != rte->relid) elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", rte->perminfoindex, perminfo->relid, rte->relid); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 5f50c87b01..3b2649f7a0 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -834,7 +834,7 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) /* Set in all RTEPermissionInfos for this query. */ foreach(l, qry->rtepermlist) { - RTEPermissionInfo *perminfo = (RTEPermissionInfo *) lfirst(l); + RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l); perminfo->checkAsUser = userid; } diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 3552a8db59..9208e8760d 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1550,7 +1550,7 @@ ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, foreach(l, src_rtable) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RangeTblEntry *rte = lfirst_node(RangeTblEntry, l); if (rte->perminfoindex > 0) rte->perminfoindex += offset; -- 2.30.2
v25-0006-fixup-change-ConcatRTEPermissionInfoLists-API.patch (text/x-diff, 2.3K) download From ed5ee6e0c4c990e5db391cccc4a6b09f797fe114 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Wed, 16 Nov 2022 12:26:45 +0100 Subject: [PATCH v25 6/9] fixup! change ConcatRTEPermissionInfoLists API --- src/backend/rewrite/rewriteManip.c | 12 +++++++----- src/include/rewrite/rewriteManip.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 9208e8760d..651d6540f8 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1534,19 +1534,19 @@ ReplaceVarsFromTargetList(Node *node, /* * ConcatRTEPermissionInfoLists - * Add RTEPermissionInfos found in src_rtepermlist into *dest_rtepermlist + * Add RTEPermissionInfos found in src_rtepermlist into dest_rtepermlist * * Also updates perminfoindex of the RTEs in src_rtable to point to the * "source" perminfos after they have been added into *dest_rtepermlist. */ -void -ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, +List * +ConcatRTEPermissionInfoLists(List *dest_rtepermlist, List *src_rtepermlist, List *src_rtable) { ListCell *l; - int offset = list_length(*dest_rtepermlist); + int offset = list_length(dest_rtepermlist); - *dest_rtepermlist = list_concat(*dest_rtepermlist, src_rtepermlist); + dest_rtepermlist = list_concat(dest_rtepermlist, src_rtepermlist); foreach(l, src_rtable) { @@ -1555,4 +1555,6 @@ ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, if (rte->perminfoindex > 0) rte->perminfoindex += offset; } + + return dest_rtepermlist; } diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index 0379dd9673..c32bee4634 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -83,7 +83,7 @@ extern Node *ReplaceVarsFromTargetList(Node *node, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks); -extern void ConcatRTEPermissionInfoLists(List **dest_rtepermlist, List *src_rtepermlist, +extern List *ConcatRTEPermissionInfoLists(List *dest_rtepermlist, List *src_rtepermlist, List *src_rtable); #endif /* REWRITEMANIP_H */ -- 2.30.2
v25-0007-perminfoindex-is-unsigned-also-test-for-0.patch (text/x-diff, 1.3K) download From be3517969c9cdcb55f5c754d849572b30abb4c3a Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Wed, 16 Nov 2022 12:27:46 +0100 Subject: [PATCH v25 7/9] perminfoindex is unsigned; also, test for 0 --- src/backend/parser/parse_relation.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 5fd01965a4..92ead7d8be 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -3749,14 +3749,15 @@ GetRTEPermissionInfo(List *rtepermlist, RangeTblEntry *rte) { RTEPermissionInfo *perminfo; - Assert(rte->perminfoindex > 0); - if (rte->perminfoindex > list_length(rtepermlist)) - elog(ERROR, "invalid perminfoindex %u in RTE with relid %u", + if (rte->perminfoindex == 0 || + rte->perminfoindex > list_length(rtepermlist)) + elog(ERROR, "invalid perminfoindex %d in RTE with relid %u", rte->perminfoindex, rte->relid); perminfo = list_nth_node(RTEPermissionInfo, rtepermlist, rte->perminfoindex - 1); if (perminfo->relid != rte->relid) elog(ERROR, "permission info at index %u (with relid=%u) does not match requested RTE (with relid=%u)", rte->perminfoindex, perminfo->relid, rte->relid); + return perminfo; } -- 2.30.2
v25-0008-Note-assert-is-redundant.-Remove-it.patch (text/x-diff, 983B) download From 7bad94ec52d81642f7dfc2bc380d2bd735997405 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Wed, 16 Nov 2022 12:28:40 +0100 Subject: [PATCH v25 8/9] Note assert is redundant. Remove it! --- src/backend/optimizer/util/inherit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 03e04094e0..468d37726a 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -931,7 +931,7 @@ GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) bms_singleton_member(rel->top_parent_relids); Assert(use_relid == root->parse->resultRelation); rte = planner_rt_fetch(use_relid, root); - Assert(rte->perminfoindex > 0); + Assert(rte->perminfoindex > 0); /* XXX this assert is already in GetRTEPermissionInfo */ perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); if (use_relid != rel->relid) -- 2.30.2
v25-0009-Stylistic-changes.patch (text/x-diff, 3.7K) download From e3319a0d6820c9344f01f01bdd9cd630e70b908a Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Wed, 16 Nov 2022 12:30:54 +0100 Subject: [PATCH v25 9/9] Stylistic changes --- src/backend/optimizer/util/inherit.c | 7 ++++--- src/backend/optimizer/util/relnode.c | 4 ++-- src/backend/parser/analyze.c | 3 +-- src/backend/parser/parse_relation.c | 11 ++++++----- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 468d37726a..a7b5606286 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -133,8 +133,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { - RTEPermissionInfo *root_perminfo = - GetRTEPermissionInfo(root->parse->rtepermlist, rte); + RTEPermissionInfo *perminfo; + + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); /* * Partitioned table, so set up for partitioning. @@ -147,7 +148,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, */ expand_partitioned_rtentry(root, rel, rte, rti, oldrelation, - root_perminfo->updatedCols, + perminfo->updatedCols, oldrc, lockmode); } else diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 4691f7f344..a1dedf52d5 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -233,9 +233,9 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) */ if (parent == NULL) { - RTEPermissionInfo *perminfo = - GetRTEPermissionInfo(root->parse->rtepermlist, rte); + RTEPermissionInfo *perminfo; + perminfo = GetRTEPermissionInfo(root->parse->rtepermlist, rte); rel->userid = perminfo->checkAsUser; } else diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 25324d9486..5279866f43 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -3348,8 +3348,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, lc->strength, lc->waitPolicy, pushedDown); - perminfo = - GetRTEPermissionInfo(qry->rtepermlist, rte); + perminfo = GetRTEPermissionInfo(qry->rtepermlist, rte); perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } break; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 92ead7d8be..177558a158 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1021,14 +1021,15 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { - RTEPermissionInfo *perminfo = - GetRTEPermissionInfo(pstate->p_rtepermlist, rte); + RTEPermissionInfo *perminfo; /* Make sure the rel as a whole is marked for SELECT access */ + perminfo = GetRTEPermissionInfo(pstate->p_rtepermlist, rte); perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - perminfo->selectedCols = bms_add_member(perminfo->selectedCols, - col - FirstLowInvalidHeapAttributeNumber); + perminfo->selectedCols = + bms_add_member(perminfo->selectedCols, + col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) { @@ -3731,7 +3732,7 @@ AddRTEPermissionInfo(List **rtepermlist, RangeTblEntry *rte) *rtepermlist = lappend(*rtepermlist, perminfo); - /* Note its index. */ + /* Note its index (1-based!) */ rte->perminfoindex = list_length(*rtepermlist); return perminfo; -- 2.30.2
^ permalink raw reply [nested|flat] 97+ messages in thread

* Re: ExecRTCheckPerms() and many prunable partitions
@ 2022-11-21 12:46  Amit Langote <amitlangote09@gmail.com>
  parent: Amit Langote <amitlangote09@gmail.com>
  1 sibling, 0 replies; 97+ messages in thread

From: Amit Langote @ 2022-11-21 12:46 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Ian Lawrence Barwick <barwick@gmail.com>; Tom Lane <tgl@sss.pgh.pa.us>; David Rowley <dgrowleyml@gmail.com>; Greg Stark <stark@mit.edu>; Julien Rouhaud <rjuju123@gmail.com>; PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>

On Mon, Nov 21, 2022 at 9:03 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Thu, Nov 10, 2022 at 8:58 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
> > Why do callers of add_rte_to_flat_rtable() have to modify the rte's
> > perminfoindex themselves, instead of having the function do it for them?
> > That looks strange.  But also it's odd that flatten_unplanned_rtes
> > concatenates the two lists after all the perminfoindexes have been
> > modified, rather than doing both things (adding each RTEs perminfo to
> > the global list and offsetting the index) as we walk the list, in
> > flatten_rtes_walker.  It looks like these two actions are disconnected
> > from one another, but unless I misunderstand, in reality the opposite is
> > true.
>
> OK, I have moved the step of updating perminfoindex into
> add_rte_to_flat_rtable(), where it looks up the RTEPermissionInfo for
> an RTE being added using GetRTEPermissionInfo() and lappend()'s it to
> finalrtepermlist before updating the index.  For flatten_rtes_walker()
> then to rely on that facility, I needed to make some changes to its
> arguments to pass the correct Query node to pick the rtepermlist from.
> Overall, setrefs.c changes now hopefully look saner than in the last
> version.
>
> As soon as I made that change, I noticed a bunch of ERRORs in
> regression tests due to the checks in GetRTEPermissionInfo(), though
> none that looked like live bugs.

I figured the no-live-bugs part warrants some clarification.  The
plan-time errors that I saw were caused in many cases by an RTE not
pointing into the correct list or having incorrect perminfoindex, most
or all of those cases involving views.  Passing a wrong perminfoindex
to the executor, though obviously not good and fixed in the latest
version, wasn't a problem in those cases, because none of those tests
would cause the executor to use the perminfoindex, such as by calling
GetRTEPermissionInfo().  I thought about that being problematic in
terms of our coverage of perminfoindex related code in the executor,
but don't really see how we could improve that situation as far as
views are concerned, because the executor is only concerned about
checking permissions for views and perminfoindex is irrelevant in that
path, because the RTEPermissionInfos are accessed directly from
es_rtepermlist.

-- 
Thanks, Amit Langote
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 97+ messages in thread

45 further messages in this thread omitted from this page (render size limit).
Use the per-message pages, or the t.mbox.gz byte stream, for the complete thread.


end of thread, other threads:[~2023-06-30 06:54 UTC | newest]

Thread overview: 97+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-06-30 13:33 ExecRTCheckPerms() and many prunable partitions Amit Langote <amitlangote09@gmail.com>
2021-06-30 14:33 ` David Rowley <dgrowleyml@gmail.com>
2021-06-30 14:58   ` Amit Langote <amitlangote09@gmail.com>
2021-06-30 22:51     ` David Rowley <dgrowleyml@gmail.com>
2021-07-01 15:45 ` Tom Lane <tgl@sss.pgh.pa.us>
2021-07-02 00:40   ` Amit Langote <amitlangote09@gmail.com>
2021-07-07 04:41     ` David Rowley <dgrowleyml@gmail.com>
2021-07-07 07:12       ` Amit Langote <amitlangote09@gmail.com>
2021-07-29 08:40     ` Amit Langote <amitlangote09@gmail.com>
2021-08-20 13:46       ` Amit Langote <amitlangote09@gmail.com>
2021-08-26 09:13         ` Amit Langote <amitlangote09@gmail.com>
2021-09-06 19:35           ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-09-10 03:22             ` Amit Langote <amitlangote09@gmail.com>
2021-12-20 07:13               ` Amit Langote <amitlangote09@gmail.com>
2022-01-13 03:10                 ` Julien Rouhaud <rjuju123@gmail.com>
2022-01-13 06:39                   ` Amit Langote <amitlangote09@gmail.com>
2022-01-17 11:50                     ` Amit Langote <amitlangote09@gmail.com>
2022-01-17 15:45                       ` Zhihong Yu <zyu@yugabyte.com>
2022-03-14 07:36                         ` Amit Langote <amitlangote09@gmail.com>
2022-03-23 07:03                           ` Amit Langote <amitlangote09@gmail.com>
2022-03-23 09:49                             ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-03-23 21:02                             ` Zhihong Yu <zyu@yugabyte.com>
2022-03-24 19:46                             ` David Rowley <dgrowleyml@gmail.com>
2022-03-31 03:16                               ` Amit Langote <amitlangote09@gmail.com>
2022-04-05 14:26                                 ` Greg Stark <stark@mit.edu>
2022-04-05 20:21                                   ` David Rowley <dgrowleyml@gmail.com>
2022-04-06 01:18                                     ` Amit Langote <amitlangote09@gmail.com>
2022-04-11 05:41                                       ` Amit Langote <amitlangote09@gmail.com>
2022-07-06 03:25                                         ` Amit Langote <amitlangote09@gmail.com>
2022-07-13 08:00                                           ` Amit Langote <amitlangote09@gmail.com>
2022-07-27 03:14                                             ` Amit Langote <amitlangote09@gmail.com>
2022-07-27 21:04                                               ` Tom Lane <tgl@sss.pgh.pa.us>
2022-07-27 21:18                                                 ` Tom Lane <tgl@sss.pgh.pa.us>
2022-10-04 03:45                                                   ` Amit Langote <amitlangote09@gmail.com>
2022-10-04 03:54                                                     ` Tom Lane <tgl@sss.pgh.pa.us>
2022-10-04 04:11                                                       ` Amit Langote <amitlangote09@gmail.com>
2022-10-06 13:29                                                         ` Amit Langote <amitlangote09@gmail.com>
2022-10-07 01:04                                                           ` Amit Langote <amitlangote09@gmail.com>
2022-10-07 04:25                                                             ` Amit Langote <amitlangote09@gmail.com>
2022-10-07 06:49                                                               ` Amit Langote <amitlangote09@gmail.com>
2022-10-07 07:31                                                                 ` Amit Langote <amitlangote09@gmail.com>
2022-10-15 06:00                                                                   ` Amit Langote <amitlangote09@gmail.com>
2022-11-03 23:46                                                                     ` Ian Lawrence Barwick <barwick@gmail.com>
2022-11-07 07:03                                                                       ` Amit Langote <amitlangote09@gmail.com>
2022-11-10 11:58                                                                         ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-11-16 11:44                                                                           ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-11-21 12:03                                                                           ` Amit Langote <amitlangote09@gmail.com>
2022-11-21 12:46                                                                             ` Amit Langote <amitlangote09@gmail.com>
2022-11-25 11:28                                                                             ` Amit Langote <amitlangote09@gmail.com>
2022-11-29 09:27                                                                               ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-11-29 13:37                                                                                 ` Amit Langote <amitlangote09@gmail.com>
2022-11-29 18:04                                                                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-11-30 02:56                                                                                     ` Amit Langote <amitlangote09@gmail.com>
2022-11-30 06:45                                                                                       ` Amit Langote <amitlangote09@gmail.com>
2022-11-30 08:32                                                                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-12-02 07:41                                                                                     ` Amit Langote <amitlangote09@gmail.com>
2022-12-02 07:44                                                                                       ` Amit Langote <amitlangote09@gmail.com>
2022-12-02 09:59                                                                                       ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-12-02 11:13                                                                                         ` Amit Langote <amitlangote09@gmail.com>
2022-12-05 03:09                                                                                           ` Amit Langote <amitlangote09@gmail.com>
2022-12-06 15:19                                                                                             ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-12-07 07:01                                                                                               ` Amit Langote <amitlangote09@gmail.com>
2022-12-07 08:47                                                                                                 ` Amit Langote <amitlangote09@gmail.com>
2022-12-07 11:44                                                                                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-12-07 10:42                                                                                               ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-12-07 10:50                                                                                                 ` Amit Langote <amitlangote09@gmail.com>
2022-12-01 10:49                                                                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-12-10 20:17                                                                                   ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Justin Pryzby <pryzby@telsasoft.com>
2022-12-11 09:25                                                                                     ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Amit Langote <amitlangote09@gmail.com>
2022-12-11 14:21                                                                                       ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Justin Pryzby <pryzby@telsasoft.com>
2022-12-12 06:23                                                                                       ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Amit Langote <amitlangote09@gmail.com>
2022-12-21 19:44                                                                                         ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Justin Pryzby <pryzby@telsasoft.com>
2023-01-05 21:12                                                                                           ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Justin Pryzby <pryzby@telsasoft.com>
2023-01-05 21:25                                                                                             ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Tom Lane <tgl@sss.pgh.pa.us>
2023-01-17 10:26                                                                                         ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-19 11:16                                                                                           ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Amit Langote <amitlangote09@gmail.com>
2023-01-19 12:25                                                                                             ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-02-17 12:02                                                                                       ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-02-17 12:05                                                                                         ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-02-20 07:56                                                                                         ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Amit Langote <amitlangote09@gmail.com>
2023-02-20 15:19                                                                                           ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-02-20 15:40                                                                                             ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Tom Lane <tgl@sss.pgh.pa.us>
2023-02-21 07:12                                                                                               ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Amit Langote <amitlangote09@gmail.com>
2023-06-28 07:30                                                                                                 ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Amit Langote <amitlangote09@gmail.com>
2023-06-30 06:54                                                                                                   ` Re: ExecRTCheckPerms() and many prunable partitions (checkAsUser) Amit Langote <amitlangote09@gmail.com>
2023-02-12 23:37                                                                                   ` Re: ExecRTCheckPerms() and many prunable partitions (sqlsmith) Justin Pryzby <pryzby@telsasoft.com>
2023-02-13 08:14                                                                                     ` Re: ExecRTCheckPerms() and many prunable partitions (sqlsmith) Amit Langote <amitlangote09@gmail.com>
2023-02-13 17:01                                                                                       ` Re: ExecRTCheckPerms() and many prunable partitions (sqlsmith) Tom Lane <tgl@sss.pgh.pa.us>
2023-02-14 05:44                                                                                         ` Re: ExecRTCheckPerms() and many prunable partitions (sqlsmith) Amit Langote <amitlangote09@gmail.com>
2022-10-12 13:49                                                           ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2022-10-13 08:14                                                             ` Amit Langote <amitlangote09@gmail.com>
2022-11-10 12:19                                                           ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2022-11-11 16:46                                                             ` Tom Lane <tgl@sss.pgh.pa.us>
2022-11-14 07:32                                                               ` Amit Langote <amitlangote09@gmail.com>
2022-09-07 09:23                                                 ` Amit Langote <amitlangote09@gmail.com>
2022-10-02 17:10                                                   ` Andres Freund <andres@anarazel.de>
2022-10-03 09:10                                                     ` Amit Langote <amitlangote09@gmail.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox