Fix usage of whole-row variables in WCO and RLS policy expressions.

Since WITH CHECK OPTION was introduced, ExecInitModifyTable has
initialized WCO expressions with the wrong plan node as parent -- that is,
it passed its input subplan not the ModifyTable node itself.  Up to now
we thought this was harmless, but bug #16006 from Vinay Banakar shows it's
not: if the input node is a SubqueryScan then ExecInitWholeRowVar can get
confused into doing the wrong thing.  (The fact that ExecInitWholeRowVar
contains such logic is certainly a horrid kluge that doesn't deserve to
live, but figuring out another way to do that is a task for some other day.)

Andres had already noticed the wrong-parent mistake and fixed it in commit
148e632c0, but not being aware of any user-visible consequences, he quite
reasonably didn't back-patch.  This patch is simply a back-patch of
148e632c0, plus addition of a test case based on bug #16006.  I also added
the test case to v12/HEAD, even though the bug is already fixed there.

Back-patch to all supported branches.  9.4 lacks RLS policies so the
new test case doesn't work there, but I'm pretty sure a test could be
devised based on using a whole-row Var in a plain WITH CHECK OPTION
condition.  (I lack the cycles to do so myself, though.)

Andres Freund and Tom Lane

Discussion: https://postgr.es/m/16006-99290d2e4642cbd5@postgresql.org
Discussion: https://postgr.es/m/20181205225213.hiwa3kgoxeybqcqv@alap3.anarazel.de
This commit is contained in:
Tom Lane 2019-09-12 18:29:17 -04:00
parent 614cdeaa89
commit 7f1f72c444
2 changed files with 53 additions and 0 deletions

View File

@ -3958,6 +3958,40 @@ DROP OPERATOR <<< (int, int);
DROP FUNCTION op_leak(int, int);
RESET SESSION AUTHORIZATION;
DROP TABLE rls_tbl;
-- Bug #16006: whole-row Vars in a policy don't play nice with sub-selects
SET SESSION AUTHORIZATION regress_rls_alice;
CREATE TABLE rls_tbl (a int, b int, c int);
CREATE POLICY p1 ON rls_tbl USING (rls_tbl >= ROW(1,1,1));
ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_tbl FORCE ROW LEVEL SECURITY;
INSERT INTO rls_tbl SELECT 10, 20, 30;
EXPLAIN (VERBOSE, COSTS OFF)
INSERT INTO rls_tbl
SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
QUERY PLAN
--------------------------------------------------------------------
Insert on regress_rls_schema.rls_tbl
-> Subquery Scan on ss
Output: ss.b, ss.c, NULL::integer
-> Sort
Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a
Sort Key: rls_tbl_1.a
-> Seq Scan on regress_rls_schema.rls_tbl rls_tbl_1
Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a
Filter: (rls_tbl_1.* >= '(1,1,1)'::record)
(9 rows)
INSERT INTO rls_tbl
SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
SELECT * FROM rls_tbl;
a | b | c
----+----+----
10 | 20 | 30
20 | 30 |
(2 rows)
DROP TABLE rls_tbl;
RESET SESSION AUTHORIZATION;
--
-- Clean up objects
--

View File

@ -1810,6 +1810,25 @@ DROP FUNCTION op_leak(int, int);
RESET SESSION AUTHORIZATION;
DROP TABLE rls_tbl;
-- Bug #16006: whole-row Vars in a policy don't play nice with sub-selects
SET SESSION AUTHORIZATION regress_rls_alice;
CREATE TABLE rls_tbl (a int, b int, c int);
CREATE POLICY p1 ON rls_tbl USING (rls_tbl >= ROW(1,1,1));
ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
ALTER TABLE rls_tbl FORCE ROW LEVEL SECURITY;
INSERT INTO rls_tbl SELECT 10, 20, 30;
EXPLAIN (VERBOSE, COSTS OFF)
INSERT INTO rls_tbl
SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
INSERT INTO rls_tbl
SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
SELECT * FROM rls_tbl;
DROP TABLE rls_tbl;
RESET SESSION AUTHORIZATION;
--
-- Clean up objects
--