diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index b1bbd2fd6b7..a4a2809ac70 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -118,6 +118,7 @@ remove_useless_outer_joins(PlannerInfo *root) SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); int innerrelid; int nremoved; + RangeTblEntry *rte; /* Skip if not removable */ if (!join_is_removable(root, sjinfo)) @@ -131,13 +132,11 @@ remove_useless_outer_joins(PlannerInfo *root) innerrelid = bms_singleton_member(sjinfo->syn_righthand); /* - * The removed rel had better not be one that the rest of the planner - * expects to find. It can't be, really: a result relation or a - * row-marked relation has junk columns in the query targetlist, and + * The removed rel had better not be row-marked. It can't be, really: + * a row-marked relation has junk columns in the query targetlist, and * join_is_removable would have seen those as uses of the rel from * "relation 0". But let's check. */ - Assert(innerrelid != root->parse->resultRelation); #ifdef USE_ASSERT_CHECKING foreach_node(PlanRowMark, rc, root->rowMarks) Assert(rc->rti != innerrelid && rc->prti != innerrelid); @@ -159,12 +158,15 @@ remove_useless_outer_joins(PlannerInfo *root) * As in pull_up_simple_subquery, discard no-longer-needed subqueries. * This is not just an optimization, but is necessary to prevent * subsequent processing from descending into stale subtrees and - * seeing inconsistent data. (Although simple_rte_array[] will be - * rebuilt shortly, we can still use it to access the correct RTE in - * the parse tree.) + * seeing inconsistent data. Likewise discard any securityQuals, + * which contain Vars of the removed rel. (Although + * simple_rte_array[] will be rebuilt shortly, we can still use it to + * access the correct RTE in the parse tree.) */ - if (root->simple_rte_array[innerrelid]->rtekind == RTE_SUBQUERY) - root->simple_rte_array[innerrelid]->subquery = NULL; + rte = root->simple_rte_array[innerrelid]; + if (rte->rtekind == RTE_SUBQUERY) + rte->subquery = NULL; + rte->securityQuals = NIL; /* * It's okay to keep scanning join_info_list for more removable joins, @@ -488,8 +490,13 @@ reduce_unique_semijoins(PlannerInfo *root) if (sjinfo->jointype != JOIN_SEMI) continue; - if (!bms_get_singleton_member(sjinfo->min_righthand, &innerrelid)) + /* + * We test the syntactic righthand side, since that's what identifies + * the JoinExpr we'll modify. + */ + if (!bms_get_singleton_member(sjinfo->syn_righthand, &innerrelid)) continue; + Assert(bms_equal(sjinfo->min_righthand, sjinfo->syn_righthand)); innerrel = find_base_rel(root, innerrelid); diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 9792a6c752f..a15d1cbede4 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -28,7 +28,6 @@ #include "optimizer/paths.h" #include "optimizer/placeholder.h" #include "optimizer/planmain.h" -#include "parser/parsetree.h" /* @@ -324,22 +323,6 @@ restart: */ setup_eager_aggregation(root); - /* - * If there's a result relation, initialize all_result_relids to include - * it; and if we've verified that it is non-inheriting, mark it as a leaf - * target. add_other_rels_to_query() will expand these sets if the result - * relation has children. - */ - if (parse->resultRelation) - { - RangeTblEntry *rte = rt_fetch(parse->resultRelation, parse->rtable); - - root->all_result_relids = bms_make_singleton(parse->resultRelation); - if (!rte->inh) - root->leaf_result_relids = - bms_make_singleton(parse->resultRelation); - } - /* * Now expand appendrels by adding "otherrels" for their children. We * delay this to the end so that we have as much information as possible diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c3c158a253d..a605387dcaa 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -803,8 +803,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name, root->eq_classes = NIL; root->ec_merging_done = false; root->last_rinfo_serial = 0; - root->all_result_relids = NULL; - root->leaf_result_relids = NULL; + root->all_result_relids = + parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL; + root->leaf_result_relids = NULL; /* we'll find out leaf-ness later */ root->append_rel_list = NIL; root->row_identity_vars = NIL; root->rowMarks = NIL; @@ -977,6 +978,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name, list_length(rte->securityQuals)); } + /* + * If we have now verified that the query target relation is + * non-inheriting, mark it as a leaf target. + */ + if (parse->resultRelation) + { + RangeTblEntry *rte = rt_fetch(parse->resultRelation, parse->rtable); + + if (!rte->inh) + root->leaf_result_relids = + bms_make_singleton(parse->resultRelation); + } + /* * This would be a convenient time to check access permissions for all * relations mentioned in the query, since it would be better to fail now, diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 52157d980b5..3653f00d383 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -534,8 +534,9 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) * RangeTblRef and JoinExpr. * * Also, new_index can be INVALID_VAR to indicate that we are deleting the - * given relid from the tree. In this case we should only find rt_index - * in nullingrels sets, never in any varno field. + * given relid from the tree. In this case we expect to find rt_index only + * in Relids fields (varnullingrels, phnullingrels, phrels), never in any + * field that identifies a single relation. * * NOTE: although this has the form of a walker, we cheat and modify the * nodes in-place. The given expression tree should have been copied diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index f42085e53a0..356f118a366 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -536,6 +536,17 @@ NOTICE: f_leak => awesome science fiction 9 | 22 | 1 | regress_rls_dave | awesome science fiction (4 rows) +-- a rel with RLS quals can still be removed by outer-join removal +EXPLAIN (COSTS OFF) +SELECT c.cid FROM category c LEFT JOIN document d ON c.cid = d.did; + QUERY PLAN +---------------------------------------------------- + Seq Scan on category c + InitPlan expr_1 + -> Index Scan using uaccount_pkey on uaccount + Index Cond: (pguser = CURRENT_USER) +(4 rows) + -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did; diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index ed70b1b229e..99eaba28f5a 100644 --- a/src/test/regress/sql/rowsecurity.sql +++ b/src/test/regress/sql/rowsecurity.sql @@ -307,6 +307,10 @@ SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did; SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0) WHERE f_leak(dtitle) ORDER BY did; +-- a rel with RLS quals can still be removed by outer-join removal +EXPLAIN (COSTS OFF) +SELECT c.cid FROM category c LEFT JOIN document d ON c.cid = d.did; + -- viewpoint from regress_rls_carol SET SESSION AUTHORIZATION regress_rls_carol; SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;