summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/prep')
-rw-r--r--src/backend/optimizer/prep/Makefile2
-rw-r--r--src/backend/optimizer/prep/prepjointree.c1
-rw-r--r--src/backend/optimizer/prep/prepsecurity.c486
-rw-r--r--src/backend/optimizer/prep/prepunion.c71
4 files changed, 25 insertions, 535 deletions
diff --git a/src/backend/optimizer/prep/Makefile b/src/backend/optimizer/prep/Makefile
index 5195d9b0ba7..86301bfbd32 100644
--- a/src/backend/optimizer/prep/Makefile
+++ b/src/backend/optimizer/prep/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/optimizer/prep
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = prepjointree.o prepqual.o prepsecurity.o preptlist.o prepunion.o
+OBJS = prepjointree.o prepqual.o preptlist.o prepunion.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 7cb1bc9a62c..6911177b68a 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -913,6 +913,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->processed_tlist = NIL;
subroot->grouping_map = NULL;
subroot->minmax_aggs = NIL;
+ subroot->qual_security_level = 0;
subroot->hasInheritedTarget = false;
subroot->hasRecursion = false;
subroot->wt_param_id = -1;
diff --git a/src/backend/optimizer/prep/prepsecurity.c b/src/backend/optimizer/prep/prepsecurity.c
deleted file mode 100644
index 455d2a066c8..00000000000
--- a/src/backend/optimizer/prep/prepsecurity.c
+++ /dev/null
@@ -1,486 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * prepsecurity.c
- * Routines for preprocessing security barrier quals.
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/optimizer/prep/prepsecurity.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "access/sysattr.h"
-#include "catalog/heap.h"
-#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
-#include "optimizer/prep.h"
-#include "parser/analyze.h"
-#include "parser/parsetree.h"
-#include "rewrite/rewriteManip.h"
-#include "utils/rel.h"
-
-
-typedef struct
-{
- int rt_index; /* Index of security barrier RTE */
- int sublevels_up; /* Current nesting depth */
- Relation rel; /* RTE relation at rt_index */
- List *targetlist; /* Targetlist for new subquery RTE */
- List *colnames; /* Column names in subquery RTE */
- List *vars_processed; /* List of Vars already processed */
-} security_barrier_replace_vars_context;
-
-static void expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
- RangeTblEntry *rte, Node *qual, bool targetRelation);
-
-static void security_barrier_replace_vars(Node *node,
- security_barrier_replace_vars_context *context);
-
-static bool security_barrier_replace_vars_walker(Node *node,
- security_barrier_replace_vars_context *context);
-
-
-/*
- * expand_security_quals -
- * expands any security barrier quals on RTEs in the query rtable, turning
- * them into security barrier subqueries.
- *
- * Any given RTE may have multiple security barrier quals in a list, from which
- * we create a set of nested subqueries to isolate each security barrier from
- * the others, providing protection against malicious user-defined security
- * barriers. The first security barrier qual in the list will be used in the
- * innermost subquery.
- *
- * In practice, the only RTEs that will have security barrier quals are those
- * that refer to tables with row-level security, or which are the target
- * relation of an update to an auto-updatable security barrier view. RTEs
- * that read from a security barrier view will have already been expanded by
- * the rewriter.
- */
-void
-expand_security_quals(PlannerInfo *root, List *tlist)
-{
- Query *parse = root->parse;
- int rt_index;
- ListCell *cell;
-
- /*
- * Process each RTE in the rtable list.
- *
- * We only ever modify entries in place and append to the rtable, so it is
- * safe to use a foreach loop here.
- */
- rt_index = 0;
- foreach(cell, parse->rtable)
- {
- bool targetRelation = false;
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(cell);
-
- rt_index++;
-
- if (rte->securityQuals == NIL)
- continue;
-
- /*
- * Ignore any RTEs that aren't used in the query (such RTEs may be
- * present for permissions checks).
- */
- if (rt_index != parse->resultRelation &&
- !rangeTableEntry_used((Node *) parse, rt_index, 0))
- continue;
-
- /*
- * If this RTE is the target then we need to make a copy of it before
- * expanding it. The unexpanded copy will become the new target, and
- * the original RTE will be expanded to become the source of rows to
- * update/delete.
- */
- if (rt_index == parse->resultRelation)
- {
- RangeTblEntry *newrte = copyObject(rte);
-
- /*
- * We need to let expand_security_qual know if this is the target
- * relation, as it has additional work to do in that case.
- *
- * Capture that information here as we're about to replace
- * parse->resultRelation.
- */
- targetRelation = true;
-
- parse->rtable = lappend(parse->rtable, newrte);
- parse->resultRelation = list_length(parse->rtable);
-
- /*
- * Wipe out any copied security barrier quals on the new target to
- * prevent infinite recursion.
- */
- newrte->securityQuals = NIL;
-
- /*
- * 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;
-
- /*
- * For the most part, Vars referencing the original relation
- * should remain as they are, meaning that they pull OLD values
- * from the expanded RTE. But in the RETURNING list and in any
- * WITH CHECK OPTION quals, we want such Vars to represent NEW
- * values, so change them to reference the new RTE.
- */
- ChangeVarNodes((Node *) parse->returningList, rt_index,
- parse->resultRelation, 0);
-
- ChangeVarNodes((Node *) parse->withCheckOptions, rt_index,
- parse->resultRelation, 0);
- }
-
- /*
- * Process each security barrier qual in turn, starting with the
- * innermost one (the first in the list) and working outwards.
- *
- * We remove each qual from the list before processing it, so that its
- * variables aren't modified by expand_security_qual. Also we don't
- * necessarily want the attributes referred to by the qual to be
- * exposed by the newly built subquery.
- */
- while (rte->securityQuals != NIL)
- {
- Node *qual = (Node *) linitial(rte->securityQuals);
-
- rte->securityQuals = list_delete_first(rte->securityQuals);
-
- ChangeVarNodes(qual, rt_index, 1, 0);
- expand_security_qual(root, tlist, rt_index, rte, qual,
- targetRelation);
- }
- }
-}
-
-
-/*
- * expand_security_qual -
- * expand the specified security barrier qual on a query RTE, turning the
- * RTE into a security barrier subquery.
- */
-static void
-expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
- RangeTblEntry *rte, Node *qual, bool targetRelation)
-{
- Query *parse = root->parse;
- Oid relid = rte->relid;
- Query *subquery;
- RangeTblEntry *subrte;
- RangeTblRef *subrtr;
- PlanRowMark *rc;
- security_barrier_replace_vars_context context;
- ListCell *cell;
-
- /*
- * There should only be 2 possible cases:
- *
- * 1. A relation RTE, which we turn into a subquery RTE containing all
- * referenced columns.
- *
- * 2. A subquery RTE (either from a prior call to this function or from an
- * expanded view). In this case we build a new subquery on top of it to
- * isolate this security barrier qual from any other quals.
- */
- switch (rte->rtekind)
- {
- case RTE_RELATION:
-
- /*
- * Turn the relation RTE into a security barrier subquery RTE,
- * moving all permissions checks down into the subquery.
- */
- subquery = makeNode(Query);
- subquery->commandType = CMD_SELECT;
- subquery->querySource = QSRC_INSTEAD_RULE;
-
- subrte = copyObject(rte);
- subrte->inFromCl = true;
- subrte->securityQuals = NIL;
- subquery->rtable = list_make1(subrte);
-
- subrtr = makeNode(RangeTblRef);
- subrtr->rtindex = 1;
- subquery->jointree = makeFromExpr(list_make1(subrtr), qual);
- subquery->hasSubLinks = checkExprHasSubLink(qual);
-
- rte->rtekind = RTE_SUBQUERY;
- rte->relid = InvalidOid;
- rte->subquery = subquery;
- rte->security_barrier = true;
- rte->inh = false; /* must not be set for a subquery */
-
- /* the permissions checks have now been moved down */
- rte->requiredPerms = 0;
- rte->checkAsUser = InvalidOid;
- rte->selectedCols = NULL;
- rte->insertedCols = NULL;
- rte->updatedCols = NULL;
-
- /*
- * Now deal with any PlanRowMark on this RTE by requesting a lock
- * of the same strength on the RTE copied down to the subquery.
- *
- * Note that we can only push down user-defined quals if they are
- * only using leakproof (and therefore trusted) functions and
- * operators. As a result, we may end up locking more rows than
- * strictly necessary (and, in the worst case, we could end up
- * locking all rows which pass the securityQuals). This is
- * currently documented behavior, but it'd be nice to come up with
- * a better solution some day.
- */
- rc = get_plan_rowmark(root->rowMarks, rt_index);
- if (rc != NULL)
- {
- if (rc->strength != LCS_NONE)
- applyLockingClause(subquery, 1, rc->strength,
- rc->waitPolicy, false);
- root->rowMarks = list_delete_ptr(root->rowMarks, rc);
- }
-
- /*
- * When we are replacing the target relation with a subquery, we
- * need to make sure to add a locking clause explicitly to the
- * generated subquery since there won't be any row marks against
- * the target relation itself.
- */
- if (targetRelation)
- applyLockingClause(subquery, 1, LCS_FORUPDATE,
- LockWaitBlock, false);
-
- /*
- * Replace any variables in the outer query that refer to the
- * original relation RTE with references to columns that we will
- * expose in the new subquery, building the subquery's targetlist
- * as we go. Also replace any references in the translated_vars
- * lists of any appendrels.
- */
- context.rt_index = rt_index;
- context.sublevels_up = 0;
- context.rel = heap_open(relid, NoLock);
- context.targetlist = NIL;
- context.colnames = NIL;
- context.vars_processed = NIL;
-
- security_barrier_replace_vars((Node *) parse, &context);
- security_barrier_replace_vars((Node *) tlist, &context);
- security_barrier_replace_vars((Node *) root->append_rel_list,
- &context);
-
- heap_close(context.rel, NoLock);
-
- /* Now we know what columns the subquery needs to expose */
- rte->subquery->targetList = context.targetlist;
- rte->eref = makeAlias(rte->eref->aliasname, context.colnames);
-
- break;
-
- case RTE_SUBQUERY:
-
- /*
- * Build a new subquery that includes all the same columns as the
- * original subquery.
- */
- subquery = makeNode(Query);
- subquery->commandType = CMD_SELECT;
- subquery->querySource = QSRC_INSTEAD_RULE;
- subquery->targetList = NIL;
-
- foreach(cell, rte->subquery->targetList)
- {
- TargetEntry *tle;
- Var *var;
-
- tle = (TargetEntry *) lfirst(cell);
- var = makeVarFromTargetEntry(1, tle);
-
- tle = makeTargetEntry((Expr *) var,
- list_length(subquery->targetList) + 1,
- pstrdup(tle->resname),
- tle->resjunk);
- subquery->targetList = lappend(subquery->targetList, tle);
- }
-
- subrte = makeNode(RangeTblEntry);
- subrte->rtekind = RTE_SUBQUERY;
- subrte->subquery = rte->subquery;
- subrte->security_barrier = rte->security_barrier;
- subrte->eref = copyObject(rte->eref);
- subrte->inFromCl = true;
- subquery->rtable = list_make1(subrte);
-
- subrtr = makeNode(RangeTblRef);
- subrtr->rtindex = 1;
- subquery->jointree = makeFromExpr(list_make1(subrtr), qual);
- subquery->hasSubLinks = checkExprHasSubLink(qual);
-
- rte->subquery = subquery;
- rte->security_barrier = true;
-
- break;
-
- default:
- elog(ERROR, "invalid range table entry for security barrier qual");
- }
-}
-
-
-/*
- * security_barrier_replace_vars -
- * Apply security barrier variable replacement to an expression tree.
- *
- * This also builds/updates a targetlist with entries for each replacement
- * variable that needs to be exposed by the security barrier subquery RTE.
- *
- * 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
- * earlier to ensure that no unwanted side-effects occur!
- */
-static void
-security_barrier_replace_vars(Node *node,
- security_barrier_replace_vars_context *context)
-{
- /*
- * Must be prepared to start with a Query or a bare expression tree; if
- * it's a Query, go straight to query_tree_walker to make sure that
- * sublevels_up doesn't get incremented prematurely.
- */
- if (node && IsA(node, Query))
- query_tree_walker((Query *) node,
- security_barrier_replace_vars_walker,
- (void *) context, 0);
- else
- security_barrier_replace_vars_walker(node, context);
-}
-
-static bool
-security_barrier_replace_vars_walker(Node *node,
- security_barrier_replace_vars_context *context)
-{
- if (node == NULL)
- return false;
-
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- /*
- * Note that the same Var may be present in different lists, so we
- * need to take care not to process it multiple times.
- */
- if (var->varno == context->rt_index &&
- var->varlevelsup == context->sublevels_up &&
- !list_member_ptr(context->vars_processed, var))
- {
- /*
- * Found a matching variable. Make sure that it is in the subquery
- * targetlist and map its attno accordingly.
- */
- AttrNumber attno;
- ListCell *l;
- TargetEntry *tle;
- char *attname;
- Var *newvar;
-
- /* Search for the base attribute in the subquery targetlist */
- attno = InvalidAttrNumber;
- foreach(l, context->targetlist)
- {
- tle = (TargetEntry *) lfirst(l);
- attno++;
-
- Assert(IsA(tle->expr, Var));
- if (((Var *) tle->expr)->varattno == var->varattno &&
- ((Var *) tle->expr)->varcollid == var->varcollid)
- {
- /* Map the variable onto this subquery targetlist entry */
- var->varattno = var->varoattno = attno;
- /* Mark this var as having been processed */
- context->vars_processed = lappend(context->vars_processed, var);
- return false;
- }
- }
-
- /* Not in the subquery targetlist, so add it. Get its name. */
- if (var->varattno < 0)
- {
- Form_pg_attribute att_tup;
-
- att_tup = SystemAttributeDefinition(var->varattno,
- context->rel->rd_rel->relhasoids);
- attname = NameStr(att_tup->attname);
- }
- else if (var->varattno == InvalidAttrNumber)
- {
- attname = "wholerow";
- }
- else if (var->varattno <= context->rel->rd_att->natts)
- {
- Form_pg_attribute att_tup;
-
- att_tup = context->rel->rd_att->attrs[var->varattno - 1];
- attname = NameStr(att_tup->attname);
- }
- else
- {
- elog(ERROR, "invalid attribute number %d in security_barrier_replace_vars", var->varattno);
- }
-
- /* New variable for subquery targetlist */
- newvar = copyObject(var);
- newvar->varno = newvar->varnoold = 1;
- newvar->varlevelsup = 0;
-
- attno = list_length(context->targetlist) + 1;
- tle = makeTargetEntry((Expr *) newvar,
- attno,
- pstrdup(attname),
- false);
-
- context->targetlist = lappend(context->targetlist, tle);
-
- context->colnames = lappend(context->colnames,
- makeString(pstrdup(attname)));
-
- /* Update the outer query's variable */
- var->varattno = var->varoattno = attno;
-
- /* Remember this Var so that we don't process it again */
- context->vars_processed = lappend(context->vars_processed, var);
- }
- return false;
- }
-
- if (IsA(node, Query))
- {
- /* Recurse into subselects */
- bool result;
-
- context->sublevels_up++;
- result = query_tree_walker((Query *) node,
- security_barrier_replace_vars_walker,
- (void *) context, 0);
- context->sublevels_up--;
- return result;
- }
-
- return expression_tree_walker(node, security_barrier_replace_vars_walker,
- (void *) context);
-}
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 1bbbc297948..06e843dff07 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -56,7 +56,6 @@ typedef struct
{
PlannerInfo *root;
AppendRelInfo *appinfo;
- int sublevels_up;
} adjust_appendrel_attrs_context;
static Path *recurse_set_operations(Node *setOp, PlannerInfo *root,
@@ -1467,12 +1466,19 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
* We copy most fields of the parent's RTE, but replace relation OID
* and relkind, and set inh = false. 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.
*/
childrte = copyObject(rte);
childrte->relid = childOID;
childrte->relkind = newrelation->rd_rel->relkind;
childrte->inh = false;
childrte->requiredPerms = 0;
+ childrte->securityQuals = NIL;
parse->rtable = lappend(parse->rtable, childrte);
childRTindex = list_length(parse->rtable);
@@ -1541,7 +1547,8 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
/*
* If all the children were temp tables, pretend it's a non-inheritance
* situation. The duplicate RTE we added for the parent table is
- * harmless, so we don't bother to get rid of it.
+ * harmless, so we don't bother to get rid of it; ditto for the useless
+ * PlanRowMark node.
*/
if (list_length(appinfos) < 2)
{
@@ -1717,9 +1724,8 @@ translate_col_privs(const Bitmapset *parent_privs,
* child rel instead. We also update rtindexes appearing outside Vars,
* such as resultRelation and jointree relids.
*
- * Note: this is applied after conversion of sublinks to subplans in the
- * query jointree, but there may still be sublinks in the security barrier
- * quals of RTEs, so we do need to cope with recursion into sub-queries.
+ * Note: this is only applied after conversion of sublinks to subplans,
+ * so we don't need to cope with recursion into sub-queries.
*
* Note: this is not hugely different from what pullup_replace_vars() does;
* maybe we should try to fold the two routines together.
@@ -1732,12 +1738,9 @@ adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo)
context.root = root;
context.appinfo = appinfo;
- context.sublevels_up = 0;
/*
- * Must be prepared to start with a Query or a bare expression tree; if
- * it's a Query, go straight to query_tree_walker to make sure that
- * sublevels_up doesn't get incremented prematurely.
+ * Must be prepared to start with a Query or a bare expression tree.
*/
if (node && IsA(node, Query))
{
@@ -1776,7 +1779,7 @@ adjust_appendrel_attrs_mutator(Node *node,
{
Var *var = (Var *) copyObject(node);
- if (var->varlevelsup == context->sublevels_up &&
+ if (var->varlevelsup == 0 &&
var->varno == appinfo->parent_relid)
{
var->varno = appinfo->child_relid;
@@ -1793,7 +1796,6 @@ adjust_appendrel_attrs_mutator(Node *node,
if (newnode == NULL)
elog(ERROR, "attribute %d of relation \"%s\" does not exist",
var->varattno, get_rel_name(appinfo->parent_reloid));
- ((Var *) newnode)->varlevelsup += context->sublevels_up;
return newnode;
}
else if (var->varattno == 0)
@@ -1836,17 +1838,10 @@ adjust_appendrel_attrs_mutator(Node *node,
RowExpr *rowexpr;
List *fields;
RangeTblEntry *rte;
- ListCell *lc;
rte = rt_fetch(appinfo->parent_relid,
context->root->parse->rtable);
fields = (List *) copyObject(appinfo->translated_vars);
- foreach(lc, fields)
- {
- Var *field = (Var *) lfirst(lc);
-
- field->varlevelsup += context->sublevels_up;
- }
rowexpr = makeNode(RowExpr);
rowexpr->args = fields;
rowexpr->row_typeid = var->vartype;
@@ -1865,8 +1860,7 @@ adjust_appendrel_attrs_mutator(Node *node,
{
CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
- if (context->sublevels_up == 0 &&
- cexpr->cvarno == appinfo->parent_relid)
+ if (cexpr->cvarno == appinfo->parent_relid)
cexpr->cvarno = appinfo->child_relid;
return (Node *) cexpr;
}
@@ -1874,8 +1868,7 @@ adjust_appendrel_attrs_mutator(Node *node,
{
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
- if (context->sublevels_up == 0 &&
- rtr->rtindex == appinfo->parent_relid)
+ if (rtr->rtindex == appinfo->parent_relid)
rtr->rtindex = appinfo->child_relid;
return (Node *) rtr;
}
@@ -1888,8 +1881,7 @@ adjust_appendrel_attrs_mutator(Node *node,
adjust_appendrel_attrs_mutator,
(void *) context);
/* now fix JoinExpr's rtindex (probably never happens) */
- if (context->sublevels_up == 0 &&
- j->rtindex == appinfo->parent_relid)
+ if (j->rtindex == appinfo->parent_relid)
j->rtindex = appinfo->child_relid;
return (Node *) j;
}
@@ -1902,7 +1894,7 @@ adjust_appendrel_attrs_mutator(Node *node,
adjust_appendrel_attrs_mutator,
(void *) context);
/* now fix PlaceHolderVar's relid sets */
- if (phv->phlevelsup == context->sublevels_up)
+ if (phv->phlevelsup == 0)
phv->phrels = adjust_relid_set(phv->phrels,
appinfo->parent_relid,
appinfo->child_relid);
@@ -1973,29 +1965,12 @@ adjust_appendrel_attrs_mutator(Node *node,
return (Node *) newinfo;
}
- if (IsA(node, Query))
- {
- /*
- * Recurse into sublink subqueries. This should only be possible in
- * security barrier quals of top-level RTEs. All other sublinks should
- * have already been converted to subplans during expression
- * preprocessing, but this doesn't happen for security barrier quals,
- * since they are destined to become quals of a subquery RTE, which
- * will be recursively planned, and so should not be preprocessed at
- * this stage.
- *
- * We don't explicitly Assert() for securityQuals here simply because
- * it's not trivial to do so.
- */
- Query *newnode;
-
- context->sublevels_up++;
- newnode = query_tree_mutator((Query *) node,
- adjust_appendrel_attrs_mutator,
- (void *) context, 0);
- context->sublevels_up--;
- return (Node *) newnode;
- }
+ /*
+ * NOTE: we do not need to recurse into sublinks, because they should
+ * already have been converted to subplans before we see them.
+ */
+ Assert(!IsA(node, SubLink));
+ Assert(!IsA(node, Query));
return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
(void *) context);