mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Fix permission tests for views/tables proven empty by constraint exclusion.
A view defined as "select <something> where false" had the curious property that the system wouldn't check whether users had the privileges necessary to select from it. More generally, permissions checks could be skipped for tables referenced in sub-selects or views that were proven empty by constraint exclusion (although some quick testing suggests this seldom happens in cases of practical interest). This happened because the planner failed to include rangetable entries for such tables in the finished plan. This was noticed in connection with erroneous handling of materialized views, but actually the issue is quite unrelated to matviews. Therefore, revert commit200ba1667b
in favor of a more direct test for the real problem. Back-patch to 9.2 where the bug was introduced (by commit7741dd6590
).
This commit is contained in:
@ -1159,7 +1159,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||
/*
|
||||
* It's possible that constraint exclusion proved the subquery empty. If
|
||||
* so, it's convenient to turn it back into a dummy path so that we will
|
||||
* recognize appropriate optimizations at this level.
|
||||
* recognize appropriate optimizations at this query level. (But see
|
||||
* create_append_plan in createplan.c, which has to reverse this
|
||||
* substitution.)
|
||||
*/
|
||||
if (is_dummy_plan(rel->subplan))
|
||||
{
|
||||
|
@ -678,30 +678,49 @@ static Plan *
|
||||
create_append_plan(PlannerInfo *root, AppendPath *best_path)
|
||||
{
|
||||
Append *plan;
|
||||
List *tlist = build_relation_tlist(best_path->path.parent);
|
||||
RelOptInfo *rel = best_path->path.parent;
|
||||
List *tlist = build_relation_tlist(rel);
|
||||
List *subplans = NIL;
|
||||
ListCell *subpaths;
|
||||
|
||||
/*
|
||||
* It is possible for the subplans list to contain only one entry, or even
|
||||
* no entries. Handle these cases specially.
|
||||
* The subpaths list could be empty, if every child was proven empty by
|
||||
* constraint exclusion. In that case generate a dummy plan that returns
|
||||
* no rows.
|
||||
*
|
||||
* XXX ideally, if there's just one entry, we'd not bother to generate an
|
||||
* Append node but just return the single child. At the moment this does
|
||||
* not work because the varno of the child scan plan won't match the
|
||||
* parent-rel Vars it'll be asked to emit.
|
||||
* Note that an AppendPath with no members is also generated in certain
|
||||
* cases where there was no appending construct at all, but we know the
|
||||
* relation is empty (see set_dummy_rel_pathlist).
|
||||
*/
|
||||
if (best_path->subpaths == NIL)
|
||||
{
|
||||
/* Generate a Result plan with constant-FALSE gating qual */
|
||||
return (Plan *) make_result(root,
|
||||
tlist,
|
||||
(Node *) list_make1(makeBoolConst(false,
|
||||
false)),
|
||||
NULL);
|
||||
/*
|
||||
* If this is a dummy path for a subquery, we have to wrap the
|
||||
* subquery's original plan in a SubqueryScan so that setrefs.c will
|
||||
* do the right things. (In particular, it must pull up the
|
||||
* subquery's rangetable so that the executor will apply permissions
|
||||
* checks to those rels at runtime.)
|
||||
*/
|
||||
if (rel->rtekind == RTE_SUBQUERY)
|
||||
{
|
||||
Assert(is_dummy_plan(rel->subplan));
|
||||
return (Plan *) make_subqueryscan(tlist,
|
||||
NIL,
|
||||
rel->relid,
|
||||
rel->subplan);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Generate a Result plan with constant-FALSE gating qual */
|
||||
return (Plan *) make_result(root,
|
||||
tlist,
|
||||
(Node *) list_make1(makeBoolConst(false,
|
||||
false)),
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Normal case with multiple subpaths */
|
||||
/* Build the plan for each child */
|
||||
foreach(subpaths, best_path->subpaths)
|
||||
{
|
||||
Path *subpath = (Path *) lfirst(subpaths);
|
||||
@ -709,6 +728,13 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
|
||||
subplans = lappend(subplans, create_plan_recurse(root, subpath));
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX ideally, if there's just one child, we'd not bother to generate an
|
||||
* Append node but just return the single child. At the moment this does
|
||||
* not work because the varno of the child scan plan won't match the
|
||||
* parent-rel Vars it'll be asked to emit.
|
||||
*/
|
||||
|
||||
plan = make_append(subplans, tlist);
|
||||
|
||||
return (Plan *) plan;
|
||||
|
Reference in New Issue
Block a user