summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorStephen Frost2015-02-26 02:36:29 +0000
committerStephen Frost2015-02-26 02:36:29 +0000
commit6f9bd50eabb0a4960e94c83dac8855771c9f340d (patch)
tree4a453331105a9e8da5cc97756b2837ad25a8ed47 /src/backend
parent77903ede08845e55bd2a6c99b52d8da6926d6e84 (diff)
Add locking clause for SB views for update/delete
In expand_security_qual(), we were handling locking correctly when a PlanRowMark existed, but not when we were working with the target relation (which doesn't have any PlanRowMarks, but the subquery created for the security barrier quals still needs to lock the rows under it). Noted by Etsuro Fujita when working with the Postgres FDW, which wasn't properly issuing a SELECT ... FOR UPDATE to the remote side under a DELETE. Back-patch to 9.4 where updatable security barrier views were introduced. Per discussion with Etsuro and Dean Rasheed.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/optimizer/prep/prepsecurity.c35
1 files changed, 28 insertions, 7 deletions
diff --git a/src/backend/optimizer/prep/prepsecurity.c b/src/backend/optimizer/prep/prepsecurity.c
index af3ee61bef1..08309538ffa 100644
--- a/src/backend/optimizer/prep/prepsecurity.c
+++ b/src/backend/optimizer/prep/prepsecurity.c
@@ -37,7 +37,7 @@ typedef struct
} security_barrier_replace_vars_context;
static void expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
- RangeTblEntry *rte, Node *qual);
+ RangeTblEntry *rte, Node *qual, bool targetRelation);
static void security_barrier_replace_vars(Node *node,
security_barrier_replace_vars_context *context);
@@ -63,6 +63,7 @@ expand_security_quals(PlannerInfo *root, List *tlist)
Query *parse = root->parse;
int rt_index;
ListCell *cell;
+ bool targetRelation = false;
/*
* Process each RTE in the rtable list.
@@ -98,6 +99,15 @@ expand_security_quals(PlannerInfo *root, List *tlist)
{
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);
@@ -147,7 +157,8 @@ expand_security_quals(PlannerInfo *root, List *tlist)
rte->securityQuals = list_delete_first(rte->securityQuals);
ChangeVarNodes(qual, rt_index, 1, 0);
- expand_security_qual(root, tlist, rt_index, rte, qual);
+ expand_security_qual(root, tlist, rt_index, rte, qual,
+ targetRelation);
}
}
}
@@ -160,7 +171,7 @@ expand_security_quals(PlannerInfo *root, List *tlist)
*/
static void
expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
- RangeTblEntry *rte, Node *qual)
+ RangeTblEntry *rte, Node *qual, bool targetRelation)
{
Query *parse = root->parse;
Oid relid = rte->relid;
@@ -219,10 +230,11 @@ expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
* 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't push the user-defined quals down since they
- * may included untrusted functions and that means that we will
- * end up locking all rows which pass the securityQuals, even if
- * those rows don't pass the user-defined quals. This is
+ * 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.
*/
@@ -256,6 +268,15 @@ expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
}
/*
+ * 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