v6-0001-Use-root-parent-s-permissions-when-reading-child-.patch
application/octet-stream
Filename: v6-0001-Use-root-parent-s-permissions-when-reading-child-.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v6-0001
Subject: Use root parent's permissions when reading child table stats
| File | + | − |
|---|---|---|
| src/backend/nodes/outfuncs.c | 1 | 0 |
| src/backend/optimizer/util/relnode.c | 15 | 0 |
| src/backend/utils/adt/selfuncs.c | 98 | 0 |
| src/include/nodes/pathnodes.h | 4 | 0 |
| src/test/regress/expected/inherit.out | 38 | 0 |
| src/test/regress/sql/inherit.sql | 21 | 0 |
From 2b1fc96fe35e0164425387ac5c5b875e2ccd2e05 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 17 Jul 2019 18:00:15 +0900
Subject: [PATCH v6] Use root parent's permissions when reading child table
stats
Author: Dilip Kumar, Amit Langote
---
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/relnode.c | 15 ++++++
src/backend/utils/adt/selfuncs.c | 98 +++++++++++++++++++++++++++++++++++
src/include/nodes/pathnodes.h | 4 ++
src/test/regress/expected/inherit.out | 38 ++++++++++++++
src/test/regress/sql/inherit.sql | 21 ++++++++
6 files changed, 177 insertions(+)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b0dcd02ff6..9856aaa1de 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2255,6 +2255,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_BITMAPSET_FIELD(direct_lateral_relids);
WRITE_BITMAPSET_FIELD(lateral_relids);
WRITE_UINT_FIELD(relid);
+ WRITE_UINT_FIELD(inh_root_relid);
WRITE_OID_FIELD(reltablespace);
WRITE_ENUM_FIELD(rtekind, RTEKind);
WRITE_INT_FIELD(min_attr);
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 85415381fb..8a2053609d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -263,6 +263,16 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->top_parent_relids = bms_copy(parent->relids);
/*
+ * For inheritance child relations, we also need to remember
+ * the root parent.
+ */
+ if (parent->rtekind == RTE_RELATION)
+ rel->inh_root_relid = parent->inh_root_relid;
+ else
+ /* Child relation of flattened UNION ALL subquery. */
+ rel->inh_root_relid = relid;
+
+ /*
* Also propagate lateral-reference information from appendrel parent
* rels to their child rels. We intentionally give each child rel the
* same minimum parameterization, even though it's quite possible that
@@ -283,6 +293,11 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
else
{
rel->top_parent_relids = NULL;
+ /*
+ * For baserels, we set ourselves as the root, because it simplifies
+ * code elsewhere.
+ */
+ rel->inh_root_relid = relid;
rel->direct_lateral_relids = NULL;
rel->lateral_relids = NULL;
rel->lateral_referencers = NULL;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 17101298fb..611d1d2814 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -4616,6 +4616,27 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
rte->securityQuals == NIL &&
(pg_class_aclcheck(rte->relid, userid,
ACL_SELECT) == ACLCHECK_OK);
+
+ /*
+ * If the user doesn't have permissions to
+ * access an inheritance child relation, check
+ * the root parent's permissions instead, that
+ * is, of the table mentioned in the query.
+ */
+ if (!vardata->acl_ok &&
+ index->rel->relid != index->rel->inh_root_relid)
+ {
+ rte = planner_rt_fetch(index->rel->relid,
+ root);
+ Assert(rte->rtekind == RTE_RELATION);
+ userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+
+ /* see the comments above */
+ vardata->acl_ok =
+ rte->securityQuals == NIL &&
+ (pg_class_aclcheck(rte->relid, userid,
+ ACL_SELECT) == ACLCHECK_OK);
+ }
}
else
{
@@ -4678,6 +4699,7 @@ examine_simple_variable(PlannerInfo *root, Var *var,
if (HeapTupleIsValid(vardata->statsTuple))
{
Oid userid;
+ RelOptInfo *rel;
/*
* Check if user has permission to read this column. We require
@@ -4693,6 +4715,82 @@ examine_simple_variable(PlannerInfo *root, Var *var,
ACL_SELECT) == ACLCHECK_OK) ||
(pg_attribute_aclcheck(rte->relid, var->varattno, userid,
ACL_SELECT) == ACLCHECK_OK));
+
+ /*
+ * If the user doesn't have permissions to access an inheritance
+ * child relation or specifically this column, check the root
+ * parent's permissions instead, that is, of the table mentioned
+ * in the query. To do that we must find out which of the root
+ * parent's attribute it corresponds to.
+ */
+ if (!vardata->acl_ok && var->varattno > 0 &&
+ (rel = find_base_rel(root, var->varno))->inh_root_relid != var->varno)
+ {
+ AppendRelInfo *appinfo;
+ int varattno = var->varattno;
+ int parent_varattno;
+ bool found = false;
+
+ appinfo = root->append_rel_array[var->varno];
+ Assert(appinfo != NULL);
+
+ /*
+ * Partitions are mapped to their immediate parent, not the
+ * root parent, so must be ready to walk up multiple
+ * AppendRelInfos. Beware not to translate the attribute
+ * number to a flattened UNION ALL subquery parent.
+ */
+ while (appinfo &&
+ planner_rt_fetch(appinfo->parent_relid,
+ root)->rtekind == RTE_RELATION)
+ {
+ ListCell *l;
+
+ parent_varattno = 1;
+ found = false;
+ foreach(l, appinfo->translated_vars)
+ {
+ Var *childvar = lfirst_node(Var, l);
+
+ /* Ignore dropped attributes of the parent. */
+ if (childvar == NULL)
+ {
+ parent_varattno++;
+ continue;
+ }
+
+ if (varattno == childvar->varattno)
+ {
+ found = true;
+ break;
+ }
+ parent_varattno++;
+ }
+
+ varattno = parent_varattno;
+
+ /* If the parent is itself a child, continue up. */
+ appinfo = root->append_rel_array[appinfo->parent_relid];
+ }
+
+ /*
+ * In rare cases, the Var may be local to the child table, in
+ * which case, we've got to live with having no access to this
+ * column's stats.
+ */
+ if (!found)
+ return;
+
+ rte = planner_rt_fetch(rel->inh_root_relid, root);
+ userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+
+ vardata->acl_ok =
+ rte->securityQuals == NIL &&
+ ((pg_class_aclcheck(rte->relid, userid,
+ ACL_SELECT) == ACLCHECK_OK) ||
+ (pg_attribute_aclcheck(rte->relid, varattno, userid,
+ ACL_SELECT) == ACLCHECK_OK));
+ }
}
else
{
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 23a06d718e..1d48c63c75 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -489,6 +489,9 @@ typedef struct PartitionSchemeData *PartitionScheme;
*
* relid - RTE index (this is redundant with the relids field, but
* is provided for convenience of access)
+ * inh_root_relid - For otherrels, this is the RT index of inheritance
+ * root table that is mentioned in the query from which this
+ * relation originated. For baserels, it's same as relid.
* rtekind - copy of RTE's rtekind field
* min_attr, max_attr - range of valid AttrNumbers for rel
* attr_needed - array of bitmapsets indicating the highest joinrel
@@ -667,6 +670,7 @@ typedef struct RelOptInfo
/* information about a base rel (not set for join rels!) */
Index relid;
+ Index inh_root_relid;
Oid reltablespace; /* containing tablespace */
RTEKind rtekind; /* RELATION, SUBQUERY, FUNCTION, etc */
AttrNumber min_attr; /* smallest attrno of rel (often <0) */
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 44d51ed711..8e0d9e4658 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -2335,3 +2335,41 @@ explain (costs off) select * from range_parted order by a desc,b desc,c desc;
(3 rows)
drop table range_parted;
+-- Test for checking that lack of access permissions for a child table and
+-- hence its statistics doesn't affect plan shapes when the query is on the
+-- parent table
+create table permtest_parent (a int, b text, c text) partition by list (a);
+create table permtest_child (b text, a int, c text) partition by list (b);
+create table permtest_grandchild (c text, b text, a int);
+alter table permtest_child attach partition permtest_grandchild for values in ('a');
+alter table permtest_parent attach partition permtest_child for values in (1);
+insert into permtest_parent select 1, 'a', i || 'x' || i * 10 from generate_series(1, 1000) i;
+analyze permtest_parent;
+explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c like '4x5%';
+ QUERY PLAN
+------------------------------------------
+ Nested Loop
+ Join Filter: (p1.a = p2.a)
+ -> Seq Scan on permtest_grandchild p1
+ Filter: (c ~~ '4x5%'::text)
+ -> Seq Scan on permtest_grandchild p2
+(5 rows)
+
+create role regress_no_child_access;
+revoke all on permtest_grandchild from regress_no_child_access;
+grant all on permtest_parent to regress_no_child_access;
+set session authorization regress_no_child_access;
+explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c like '4x5%';
+ QUERY PLAN
+------------------------------------------
+ Nested Loop
+ Join Filter: (p1.a = p2.a)
+ -> Seq Scan on permtest_grandchild p1
+ Filter: (c ~~ '4x5%'::text)
+ -> Seq Scan on permtest_grandchild p2
+(5 rows)
+
+reset session authorization;
+revoke all on permtest_parent from regress_no_child_access;
+drop role regress_no_child_access;
+drop table permtest_parent;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 3af1bf30a7..1b1fddc47f 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -845,3 +845,24 @@ explain (costs off) select * from range_parted order by a,b,c;
explain (costs off) select * from range_parted order by a desc,b desc,c desc;
drop table range_parted;
+
+-- Test for checking that lack of access permissions for a child table and
+-- hence its statistics doesn't affect plan shapes when the query is on the
+-- parent table
+create table permtest_parent (a int, b text, c text) partition by list (a);
+create table permtest_child (b text, a int, c text) partition by list (b);
+create table permtest_grandchild (c text, b text, a int);
+alter table permtest_child attach partition permtest_grandchild for values in ('a');
+alter table permtest_parent attach partition permtest_child for values in (1);
+insert into permtest_parent select 1, 'a', i || 'x' || i * 10 from generate_series(1, 1000) i;
+analyze permtest_parent;
+explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c like '4x5%';
+create role regress_no_child_access;
+revoke all on permtest_grandchild from regress_no_child_access;
+grant all on permtest_parent to regress_no_child_access;
+set session authorization regress_no_child_access;
+explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c like '4x5%';
+reset session authorization;
+revoke all on permtest_parent from regress_no_child_access;
+drop role regress_no_child_access;
+drop table permtest_parent;
--
2.11.0