diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b96c29d8f64701e7e2f32d6cd2f6ef4815160a19..c20a17802b1f0da523fdd70263c07ec00d0e0a0a 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -186,6 +186,33 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, 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 @@ -233,6 +260,26 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, 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, diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 6fc80af30ee438ada4b2e99c782e46826d3967ec..7628e52901a270eee86cebec0bb22ee51573d4f7 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -1246,21 +1246,23 @@ NOTICE: f_leak => cde 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 @@ -1275,8 +1277,8 @@ NOTICE: f_leak => cde 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 @@ -1285,9 +1287,11 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; 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 @@ -1301,9 +1305,11 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; 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 @@ -1317,9 +1323,11 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; 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 @@ -1329,7 +1337,7 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2; 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 @@ -1960,8 +1968,6 @@ NOTICE: f_leak => fgh_updt (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 @@ -1970,15 +1976,13 @@ NOTICE: f_leak => fgh_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