Re: WIP patch (v2) for updatable security barrier views - Mailing list pgsql-hackers
From | Craig Ringer |
---|---|
Subject | Re: WIP patch (v2) for updatable security barrier views |
Date | |
Msg-id | 531D3355.6010403@2ndquadrant.com Whole thread Raw |
In response to | WIP patch for updatable security barrier views (Craig Ringer <craig@2ndquadrant.com>) |
Responses |
Re: WIP patch (v2) for updatable security barrier views
|
List | pgsql-hackers |
I've found an issue with updatable security barrier views. Locking is being pushed down into the subquery. Locking is thus applied before user-supplied quals are, so we potentially lock too many rows. I'm looking into the code now, but in the mean time, here's a demo of the problem: regress=> CREATE TABLE t1(x integer, y integer); CREATE TABLE regress=> INSERT INTO t1(x,y) VALUES (1,1), (2,2), (3,3), (4,4); INSERT 0 4 regress=> CREATE VIEW v1 WITH (security_barrier) AS SELECT x, y FROM t1 WHERE x % 2 = 0; CREATE VIEW regress=> EXPLAIN SELECT * FROM v1 FOR UPDATE; QUERY PLAN -----------------------------------------------------------------------LockRows (cost=0.00..42.43 rows=11 width=40) -> Subquery Scan on v1 (cost=0.00..42.32 rows=11 width=40) -> LockRows (cost=0.00..42.21 rows=11 width=14) -> Seq Scan on t1 (cost=0.00..42.10 rows=11 width=14) Filter: ((x % 2) = 0)Planning time: 0.140ms (6 rows) or, preventing pushdown with a wrapper function to demonstrate the problem: regress=> CREATE FUNCTION is_one(integer) RETURNS boolean AS $$ DECLARE result integer; BEGIN SELECT $1 = 1 regress=> EXPLAIN SELECT * FROM v1 WHERE is_one(x) FOR UPDATE; QUERY PLAN -----------------------------------------------------------------------LockRows (cost=0.00..45.11 rows=4 width=40) -> Subquery Scan on v1 (cost=0.00..45.07 rows=4 width=40) Filter: is_one(v1.x) -> LockRows (cost=0.00..42.21rows=11 width=14) -> Seq Scan on t1 (cost=0.00..42.10 rows=11 width=14) Filter: ((x % 2) = 0)Planning time: 0.147 ms (7 rows) OK, so it looks like the code: /* * Now deal with any PlanRowMark on this RTE by requesting a lock * of the same strength on the RTE copied down to the subquery. */ rc = get_plan_rowmark(root->rowMarks,rt_index); if (rc != NULL) { switch (rc->markType) { /* .... */ } root->rowMarks = list_delete(root->rowMarks, rc); } isn't actually appropriate. We should _not_ be pushing locking down into the subquery. Instead, we should be retargeting the rowmark so it points to the new subquery RTE, marking rows _after_filtering. We want a plan like: regress=> EXPLAIN SELECT * FROM v1 WHERE is_one(x) FOR UPDATE; QUERY PLAN -----------------------------------------------------------------------LockRows (cost=0.00..45.11 rows=4 width=40) -> Subquery Scan on v1 (cost=0.00..45.07 rows=4 width=40) Filter: is_one(v1.x) -> Seq Scan on t1 (cost=0.00..42.10rows=11 width=14) Filter: ((x % 2) = 0)Planning time: 0.147 ms (7 rows) I'm not too sure what the best way to do that is. Time permitting I'll see if I can work out the RowMark code and set something up. -- Craig Ringer http://www.2ndQuadrant.com/PostgreSQL Development, 24x7 Support, Training & Services
pgsql-hackers by date: