@@ -165,18 +165,58 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
165165 commandType = rt_index == root -> resultRelation ?
166166 root -> commandType : CMD_SELECT ;
167167
168- get_policies_for_relation (rel , commandType , user_id , & permissive_policies ,
169- & restrictive_policies );
168+ /*
169+ * In some cases, we need to apply USING policies (which control the
170+ * visibility of records) associated with multiple command types (see
171+ * specific cases below).
172+ *
173+ * When considering the order in which to apply these USING policies,
174+ * we prefer to apply higher privileged policies, those which allow the
175+ * user to lock records (UPDATE and DELETE), first, followed by policies
176+ * which don't (SELECT).
177+ *
178+ * Note that the optimizer is free to push down and reorder quals which
179+ * use leakproof functions.
180+ *
181+ * In all cases, if there are no policy clauses allowing access to rows in
182+ * the table for the specific type of operation, then a single always-false
183+ * clause (a default-deny policy) will be added (see add_security_quals).
184+ */
185+
186+ /*
187+ * For a SELECT, if UPDATE privileges are required (eg: the user has
188+ * specified FOR [KEY] UPDATE/SHARE), then add the UPDATE USING quals first.
189+ *
190+ * This way, we filter out any records from the SELECT FOR SHARE/UPDATE
191+ * which the user does not have access to via the UPDATE USING policies,
192+ * similar to how we require normal UPDATE rights for these queries.
193+ */
194+ if (commandType == CMD_SELECT && rte -> requiredPerms & ACL_UPDATE )
195+ {
196+ List * update_permissive_policies ;
197+ List * update_restrictive_policies ;
198+
199+ get_policies_for_relation (rel , CMD_UPDATE , user_id ,
200+ & update_permissive_policies ,
201+ & update_restrictive_policies );
202+
203+ add_security_quals (rt_index ,
204+ update_permissive_policies ,
205+ update_restrictive_policies ,
206+ securityQuals ,
207+ hasSubLinks );
208+ }
170209
171210 /*
172- * For SELECT, UPDATE and DELETE, add security quals to enforce these
211+ * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
173212 * policies. These security quals control access to existing table rows.
174213 * Restrictive policies are "AND"d together, and permissive policies are
175214 * "OR"d together.
176- *
177- * If there are no policy clauses controlling access to the table, this
178- * will add a single always-false clause (a default-deny policy).
179215 */
216+
217+ get_policies_for_relation (rel , commandType , user_id , & permissive_policies ,
218+ & restrictive_policies );
219+
180220 if (commandType == CMD_SELECT ||
181221 commandType == CMD_UPDATE ||
182222 commandType == CMD_DELETE )
@@ -187,28 +227,27 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
187227 hasSubLinks );
188228
189229 /*
190- * For the target relation, when there is a returning list, we need to
191- * collect up CMD_SELECT policies and add them via add_security_quals.
192- * This is because, for the RETURNING case , we have to filter any records
193- * which are not visible through an ALL or SELECT USING policy .
230+ * Similar to above, during an UPDATE or DELETE, if SELECT rights are also
231+ * required (eg: when a RETURNING clause exists, or the user has provided
232+ * a WHERE clause which involves columns from the relation) , we collect up
233+ * CMD_SELECT policies and add them via add_security_quals first .
194234 *
195- * We don't need to worry about the non-target relation case because we are
196- * checking the ALL and SELECT policies for those relations anyway (see
197- * above).
235+ * This way, we filter out any records which are not visible through an ALL
236+ * or SELECT USING policy.
198237 */
199- if (root -> returningList != NIL &&
200- ( commandType == CMD_UPDATE || commandType == CMD_DELETE ) )
238+ if (( commandType == CMD_UPDATE || commandType == CMD_DELETE ) &&
239+ rte -> requiredPerms & ACL_SELECT )
201240 {
202- List * returning_permissive_policies ;
203- List * returning_restrictive_policies ;
241+ List * select_permissive_policies ;
242+ List * select_restrictive_policies ;
204243
205244 get_policies_for_relation (rel , CMD_SELECT , user_id ,
206- & returning_permissive_policies ,
207- & returning_restrictive_policies );
245+ & select_permissive_policies ,
246+ & select_restrictive_policies );
208247
209248 add_security_quals (rt_index ,
210- returning_permissive_policies ,
211- returning_restrictive_policies ,
249+ select_permissive_policies ,
250+ select_restrictive_policies ,
212251 securityQuals ,
213252 hasSubLinks );
214253 }
@@ -261,21 +300,22 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
261300 hasSubLinks );
262301
263302 /*
264- * Get and add ALL/SELECT policies, if there is a RETURNING clause,
265- * also as WCO policies, again, to avoid silently dropping data.
303+ * Get and add ALL/SELECT policies, if SELECT rights are required
304+ * for this relation, also as WCO policies, again, to avoid
305+ * silently dropping data. See above.
266306 */
267- if (root -> returningList != NIL )
307+ if (rte -> requiredPerms & ACL_SELECT )
268308 {
269- List * conflict_returning_permissive_policies = NIL ;
270- List * conflict_returning_restrictive_policies = NIL ;
309+ List * conflict_select_permissive_policies = NIL ;
310+ List * conflict_select_restrictive_policies = NIL ;
271311
272312 get_policies_for_relation (rel , CMD_SELECT , user_id ,
273- & conflict_returning_permissive_policies ,
274- & conflict_returning_restrictive_policies );
313+ & conflict_select_permissive_policies ,
314+ & conflict_select_restrictive_policies );
275315 add_with_check_options (rel , rt_index ,
276316 WCO_RLS_CONFLICT_CHECK ,
277- conflict_returning_permissive_policies ,
278- conflict_returning_restrictive_policies ,
317+ conflict_select_permissive_policies ,
318+ conflict_select_restrictive_policies ,
279319 withCheckOptions ,
280320 hasSubLinks );
281321 }
@@ -524,7 +564,7 @@ add_security_quals(int rt_index,
524564 qual = copyObject (policy -> qual );
525565 ChangeVarNodes ((Node * ) qual , 1 , rt_index , 0 );
526566
527- * securityQuals = lappend (* securityQuals , qual );
567+ * securityQuals = list_append_unique (* securityQuals , qual );
528568 * hasSubLinks |= policy -> hassublinks ;
529569 }
530570 }
@@ -539,7 +579,7 @@ add_security_quals(int rt_index,
539579 rowsec_expr = makeBoolExpr (OR_EXPR , permissive_quals , -1 );
540580
541581 ChangeVarNodes ((Node * ) rowsec_expr , 1 , rt_index , 0 );
542- * securityQuals = lappend (* securityQuals , rowsec_expr );
582+ * securityQuals = list_append_unique (* securityQuals , rowsec_expr );
543583 }
544584 else
545585 /*
@@ -631,7 +671,7 @@ add_with_check_options(Relation rel,
631671
632672 ChangeVarNodes (wco -> qual , 1 , rt_index , 0 );
633673
634- * withCheckOptions = lappend (* withCheckOptions , wco );
674+ * withCheckOptions = list_append_unique (* withCheckOptions , wco );
635675
636676 /*
637677 * Now add WithCheckOptions for each of the restrictive policy clauses
@@ -657,7 +697,7 @@ add_with_check_options(Relation rel,
657697 wco -> qual = (Node * ) qual ;
658698 wco -> cascaded = false;
659699
660- * withCheckOptions = lappend (* withCheckOptions , wco );
700+ * withCheckOptions = list_append_unique (* withCheckOptions , wco );
661701 * hasSubLinks |= policy -> hassublinks ;
662702 }
663703 }
0 commit comments