aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/path/allpaths.c4
-rw-r--r--src/backend/optimizer/plan/createplan.c33
-rw-r--r--src/backend/optimizer/plan/setrefs.c251
-rw-r--r--src/test/regress/expected/privileges.out15
-rw-r--r--src/test/regress/sql/privileges.sql15
5 files changed, 233 insertions, 85 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 474cd7b6eba..105718ff371 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1159,9 +1159,7 @@ 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 query level. (But see
- * create_append_plan in createplan.c, which has to reverse this
- * substitution.)
+ * recognize appropriate optimizations at this query level.
*/
if (is_dummy_plan(rel->subplan))
{
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e43ef3aa898..52bab79007e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -678,8 +678,7 @@ static Plan *
create_append_plan(PlannerInfo *root, AppendPath *best_path)
{
Append *plan;
- RelOptInfo *rel = best_path->path.parent;
- List *tlist = build_relation_tlist(rel);
+ List *tlist = build_relation_tlist(best_path->path.parent);
List *subplans = NIL;
ListCell *subpaths;
@@ -694,30 +693,12 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
*/
if (best_path->subpaths == NIL)
{
- /*
- * 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);
- }
+ /* Generate a Result plan with constant-FALSE gating qual */
+ return (Plan *) make_result(root,
+ tlist,
+ (Node *) list_make1(makeBoolConst(false,
+ false)),
+ NULL);
}
/* Build the plan for each child */
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 6b8faf52b0a..b78d72701b3 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -21,6 +21,7 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/tlist.h"
#include "tcop/utility.h"
#include "utils/lsyscache.h"
@@ -81,6 +82,10 @@ typedef struct
#define fix_scan_list(root, lst, rtoffset) \
((List *) fix_scan_expr(root, (Node *) (lst), rtoffset))
+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_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,
IndexOnlyScan *plan,
@@ -196,63 +201,11 @@ set_plan_references(PlannerInfo *root, Plan *plan)
ListCell *lc;
/*
- * 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 alias and eref Alias fields, which
- * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
- * which are needed for executor-startup permissions checking and for
- * trigger event checking.
+ * Add all the query's RTEs to the flattened rangetable. The live ones
+ * will have their rangetable indexes increased by rtoffset. (Additional
+ * RTEs, not referenced by the Plan tree, might get added after those.)
*/
- foreach(lc, root->parse->rtable)
- {
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
- RangeTblEntry *newrte;
-
- /* flat copy to duplicate all the scalar fields */
- newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry));
- memcpy(newrte, rte, sizeof(RangeTblEntry));
-
- /* zap unneeded sub-structure */
- newrte->subquery = NULL;
- newrte->joinaliasvars = NIL;
- newrte->funcexpr = NULL;
- newrte->funccoltypes = NIL;
- newrte->funccoltypmods = NIL;
- newrte->funccolcollations = NIL;
- newrte->values_lists = NIL;
- newrte->values_collations = NIL;
- newrte->ctecoltypes = NIL;
- newrte->ctecoltypmods = NIL;
- newrte->ctecolcollations = NIL;
-
- glob->finalrtable = lappend(glob->finalrtable, newrte);
-
- /*
- * If it's a plain relation RTE, add the table to relationOids.
- *
- * We do this even though the RTE might be unreferenced in the plan
- * tree; this would correspond to cases such as views that were
- * expanded, child tables that were eliminated by constraint
- * exclusion, etc. Schema invalidation on such a rel must still force
- * rebuilding of the plan.
- *
- * Note we don't bother to avoid duplicate list entries. We could,
- * but it would probably cost more cycles than it would save.
- */
- if (newrte->rtekind == RTE_RELATION)
- glob->relationOids = lappend_oid(glob->relationOids,
- newrte->relid);
- }
-
- /*
- * Check for RT index overflow; it's very unlikely, but if it did happen,
- * the executor would get confused by varnos that match the special varno
- * values.
- */
- if (IS_SPECIAL_VARNO(list_length(glob->finalrtable)))
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("too many range table entries")));
+ add_rtes_to_flat_rtable(root, false);
/*
* Adjust RT indexes of PlanRowMarks and add to final rowmarks list
@@ -280,6 +233,192 @@ 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.
+ */
+static void
+add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
+{
+ PlannerGlobal *glob = root->glob;
+ Index rti;
+ ListCell *lc;
+
+ /*
+ * 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);
+
+ /*
+ * We should ignore inheritance-parent RTEs: their contents have been
+ * pulled up into our rangetable already. Also ignore any subquery
+ * RTEs without matching RelOptInfos, as they likewise have been
+ * pulled up.
+ */
+ if (rte->rtekind == RTE_SUBQUERY && !rte->inh)
+ {
+ RelOptInfo *rel = root->simple_rel_array[rti];
+
+ if (rel != NULL)
+ {
+ Assert(rel->relid == rti); /* sanity check on array */
+
+ /*
+ * The subquery might never have been 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 plan 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 subplan 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.
+ */
+ if (rel->subplan == NULL)
+ flatten_unplanned_rtes(glob, rte);
+ else if (recursing || is_dummy_plan(rel->subplan))
+ {
+ Assert(rel->subroot != NULL);
+ add_rtes_to_flat_rtable(rel->subroot, true);
+ }
+ }
+ }
+ 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);
+}
+
+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);
+ }
+ return expression_tree_walker(node, flatten_rtes_walker,
+ (void *) glob);
+}
+
+/*
+ * Add (a copy of) the given RTE to the final rangetable
+ *
+ * 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 alias and eref Alias fields, which
+ * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
+ * which are needed for executor-startup permissions checking and for
+ * trigger event checking.
+ */
+static void
+add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
+{
+ RangeTblEntry *newrte;
+
+ /* flat copy to duplicate all the scalar fields */
+ newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry));
+ memcpy(newrte, rte, sizeof(RangeTblEntry));
+
+ /* zap unneeded sub-structure */
+ newrte->subquery = NULL;
+ newrte->joinaliasvars = NIL;
+ newrte->funcexpr = NULL;
+ newrte->funccoltypes = NIL;
+ newrte->funccoltypmods = NIL;
+ newrte->funccolcollations = NIL;
+ newrte->values_lists = NIL;
+ newrte->values_collations = NIL;
+ newrte->ctecoltypes = NIL;
+ newrte->ctecoltypmods = NIL;
+ newrte->ctecolcollations = NIL;
+
+ glob->finalrtable = lappend(glob->finalrtable, newrte);
+
+ /*
+ * Check for RT index overflow; it's very unlikely, but if it did happen,
+ * the executor would get confused by varnos that match the special varno
+ * values.
+ */
+ if (IS_SPECIAL_VARNO(list_length(glob->finalrtable)))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many range table entries")));
+
+ /*
+ * If it's a plain relation RTE, add the table to relationOids.
+ *
+ * We do this even though the RTE might be unreferenced in the plan tree;
+ * this would correspond to cases such as views that were expanded, child
+ * tables that were eliminated by constraint exclusion, etc. Schema
+ * invalidation on such a rel must still force rebuilding of the plan.
+ *
+ * Note we don't bother to avoid making duplicate list entries. We could,
+ * but it would probably cost more cycles than it would save.
+ */
+ if (newrte->rtekind == RTE_RELATION)
+ glob->relationOids = lappend_oid(glob->relationOids, newrte->relid);
+}
+
+/*
* set_plan_refs: recurse through the Plan nodes of a single subquery level
*/
static Plan *
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 68afecc91f7..3da03fc9ae2 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -228,6 +228,21 @@ SELECT * FROM atestv3; -- ok
SELECT * FROM atestv0; -- fail
ERROR: permission denied for relation atestv0
+-- Appendrels excluded by constraints failed to check permissions in 8.4-9.2.
+select * from
+ ((select a.q1 as x from int8_tbl a offset 0)
+ union all
+ (select b.q2 as x from int8_tbl b offset 0)) ss
+where false;
+ERROR: permission denied for relation int8_tbl
+set constraint_exclusion = on;
+select * from
+ ((select a.q1 as x, random() from int8_tbl a where q1 > 0)
+ union all
+ (select b.q2 as x, random() from int8_tbl b where q2 > 0)) ss
+where x < 0;
+ERROR: permission denied for relation int8_tbl
+reset constraint_exclusion;
CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view
SELECT * FROM atestv4; -- ok
one | two | three
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 6ac3378a8d0..cb993ae2b09 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -162,6 +162,21 @@ SELECT * FROM atestv2; -- fail
SELECT * FROM atestv3; -- ok
SELECT * FROM atestv0; -- fail
+-- Appendrels excluded by constraints failed to check permissions in 8.4-9.2.
+select * from
+ ((select a.q1 as x from int8_tbl a offset 0)
+ union all
+ (select b.q2 as x from int8_tbl b offset 0)) ss
+where false;
+
+set constraint_exclusion = on;
+select * from
+ ((select a.q1 as x, random() from int8_tbl a where q1 > 0)
+ union all
+ (select b.q2 as x, random() from int8_tbl b where q2 > 0)) ss
+where x < 0;
+reset constraint_exclusion;
+
CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view
SELECT * FROM atestv4; -- ok
GRANT SELECT ON atestv4 TO regressuser2;