securityQuals,
hasSubLinks);
+ /*
+ * For the target relation, when there is a returning list, we need to
+ * collect up CMD_SELECT policies and add them via add_security_quals.
+ * This is because, for the RETURNING case, we have to filter any records
+ * which are not visible through an ALL or SELECT USING policy.
+ *
+ * We don't need to worry about the non-target relation case because we are
+ * checking the ALL and SELECT policies for those relations anyway (see
+ * above).
+ */
+ if (root->returningList != NIL &&
+ (commandType == CMD_UPDATE || commandType == CMD_DELETE))
+ {
+ List *returning_permissive_policies;
+ List *returning_restrictive_policies;
+
+ get_policies_for_relation(rel, CMD_SELECT, user_id,
+ &returning_permissive_policies,
+ &returning_restrictive_policies);
+
+ add_security_quals(rt_index,
+ returning_permissive_policies,
+ returning_restrictive_policies,
+ securityQuals,
+ hasSubLinks);
+ }
+
/*
* For INSERT and UPDATE, add withCheckOptions to verify that any new
* records added are consistent with the security policies. This will use
withCheckOptions,
hasSubLinks);
+ /*
+ * Get and add ALL/SELECT policies, if there is a RETURNING clause,
+ * also as WCO policies, again, to avoid silently dropping data.
+ */
+ if (root->returningList != NIL)
+ {
+ List *conflict_returning_permissive_policies = NIL;
+ List *conflict_returning_restrictive_policies = NIL;
+
+ get_policies_for_relation(rel, CMD_SELECT, user_id,
+ &conflict_returning_permissive_policies,
+ &conflict_returning_restrictive_policies);
+ add_with_check_options(rel, rt_index,
+ WCO_RLS_CONFLICT_CHECK,
+ conflict_returning_permissive_policies,
+ conflict_returning_restrictive_policies,
+ withCheckOptions,
+ hasSubLinks);
+ }
+
/* Enforce the WITH CHECK clauses of the UPDATE policies */
add_with_check_options(rel, rt_index,
WCO_RLS_UPDATE_CHECK,
EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
- QUERY PLAN
----------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------
Update on t2 t2_1_1
-> Nested Loop
Join Filter: (t2_1.b = t2_2.b)
-> Subquery Scan on t2_1
Filter: f_leak(t2_1.b)
- -> LockRows
- -> Seq Scan on t2 t2_1_2
- Filter: ((a = 3) AND ((a % 2) = 1))
+ -> Subquery Scan on t2_1_2
+ Filter: ((t2_1_2.a % 2) = 1)
+ -> LockRows
+ -> Seq Scan on t2 t2_1_3
+ Filter: ((a = 3) AND ((a % 2) = 1))
-> Subquery Scan on t2_2
Filter: f_leak(t2_2.b)
-> Seq Scan on t2 t2_2_1
Filter: ((a = 3) AND ((a % 2) = 1))
-(12 rows)
+(14 rows)
UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
- QUERY PLAN
----------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------
Update on t1 t1_1_3
Update on t1 t1_1_3
Update on t2 t1_1
Join Filter: (t1_1.b = t1_2.b)
-> Subquery Scan on t1_1
Filter: f_leak(t1_1.b)
- -> LockRows
- -> Seq Scan on t1 t1_1_4
- Filter: ((a = 4) AND ((a % 2) = 0))
+ -> Subquery Scan on t1_1_4
+ Filter: ((t1_1_4.a % 2) = 0)
+ -> LockRows
+ -> Seq Scan on t1 t1_1_5
+ Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_2
Filter: f_leak(t1_2.b)
-> Append
Join Filter: (t1_1_1.b = t1_2_1.b)
-> Subquery Scan on t1_1_1
Filter: f_leak(t1_1_1.b)
- -> LockRows
- -> Seq Scan on t2 t1_1_5
- Filter: ((a = 4) AND ((a % 2) = 0))
+ -> Subquery Scan on t1_1_6
+ Filter: ((t1_1_6.a % 2) = 0)
+ -> LockRows
+ -> Seq Scan on t2 t1_1_7
+ Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_2_1
Filter: f_leak(t1_2_1.b)
-> Append
Join Filter: (t1_1_2.b = t1_2_2.b)
-> Subquery Scan on t1_1_2
Filter: f_leak(t1_1_2.b)
- -> LockRows
- -> Seq Scan on t3 t1_1_6
- Filter: ((a = 4) AND ((a % 2) = 0))
+ -> Subquery Scan on t1_1_8
+ Filter: ((t1_1_8.a % 2) = 0)
+ -> LockRows
+ -> Seq Scan on t3 t1_1_9
+ Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_2_2
Filter: f_leak(t1_2_2.b)
-> Append
Filter: ((a = 4) AND ((a % 2) = 0))
-> Seq Scan on t3 t1_2_11
Filter: ((a = 4) AND ((a % 2) = 0))
-(52 rows)
+(58 rows)
UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
(6 rows)
DELETE FROM x1 WHERE f_leak(b) RETURNING *;
-NOTICE: f_leak => abc_updt
-NOTICE: f_leak => efg_updt
NOTICE: f_leak => cde_updt
NOTICE: f_leak => fgh_updt
NOTICE: f_leak => bcd_updt_updt
NOTICE: f_leak => fgh_updt_updt
a | b | c
---+---------------+-------------------
- 1 | abc_updt | rls_regress_user1
- 5 | efg_updt | rls_regress_user1
3 | cde_updt | rls_regress_user2
7 | fgh_updt | rls_regress_user2
2 | bcd_updt_updt | rls_regress_user1
4 | def_updt_updt | rls_regress_user2
6 | fgh_updt_updt | rls_regress_user1
8 | fgh_updt_updt | rls_regress_user2
-(8 rows)
+(6 rows)
--
-- Duplicate Policy Names