Correctly update hasSubLinks while mutating a rule action.

rewriteRuleAction neglected to check for SubLink nodes in the
securityQuals of range table entries.  This could lead to failing
to convert such a SubLink to a SubPlan, resulting in assertion
crashes or weird errors later in planning.

In passing, fix some poor coding in rewriteTargetView:
we should not pass the source parsetree's hasSubLinks
field to ReplaceVarsFromTargetList's outer_hasSubLinks.
ReplaceVarsFromTargetList knows enough to ignore that
when a Query node is passed, but it's still confusing
and bad precedent: if we did try to update that flag
we'd be updating a stale copy of the parsetree.

Per bug #17972 from Alexander Lakhin.  This has been broken since
we added RangeTblEntry.securityQuals (although the presented test
case only fails back to 215b43cdc), so back-patch all the way.

Discussion: https://postgr.es/m/17972-f422c094237847d0@postgresql.org
This commit is contained in:
Tom Lane 2023-06-13 15:58:37 -04:00
parent bbfc26d861
commit 13192a324a
3 changed files with 52 additions and 1 deletions

View File

@ -468,6 +468,8 @@ rewriteRuleAction(Query *parsetree,
/* other RTE types don't contain bare expressions */
break;
}
sub_action->hasSubLinks |=
checkExprHasSubLink((Node *) rte->securityQuals);
if (sub_action->hasSubLinks)
break; /* no need to keep scanning rtable */
}
@ -3229,7 +3231,7 @@ rewriteTargetView(Query *parsetree, Relation view)
view_targetlist,
REPLACEVARS_REPORT_ERROR,
0,
&parsetree->hasSubLinks);
NULL);
/*
* Update all other RTI references in the query that point to the view

View File

@ -2522,6 +2522,38 @@ DROP VIEW v1;
DROP TABLE t2;
DROP TABLE t1;
--
-- Test sub-select in nested security barrier views, per bug #17972
--
CREATE TABLE t1 (a int);
CREATE VIEW v1 WITH (security_barrier = true) AS
SELECT * FROM t1;
CREATE RULE v1_upd_rule AS ON UPDATE TO v1 DO INSTEAD
UPDATE t1 SET a = NEW.a WHERE a = OLD.a;
CREATE VIEW v2 WITH (security_barrier = true) AS
SELECT * FROM v1 WHERE EXISTS (SELECT 1);
EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1;
QUERY PLAN
---------------------------------------------------
Update on t1
InitPlan 1 (returns $0)
-> Result
-> Merge Join
Merge Cond: (t1.a = v1.a)
-> Sort
Sort Key: t1.a
-> Seq Scan on t1
-> Sort
Sort Key: v1.a
-> Subquery Scan on v1
-> Result
One-Time Filter: $0
-> Seq Scan on t1 t1_1
(14 rows)
DROP VIEW v2;
DROP VIEW v1;
DROP TABLE t1;
--
-- Test CREATE OR REPLACE VIEW turning a non-updatable view into an
-- auto-updatable view and adding check options in a single step
--

View File

@ -1206,6 +1206,23 @@ DROP VIEW v1;
DROP TABLE t2;
DROP TABLE t1;
--
-- Test sub-select in nested security barrier views, per bug #17972
--
CREATE TABLE t1 (a int);
CREATE VIEW v1 WITH (security_barrier = true) AS
SELECT * FROM t1;
CREATE RULE v1_upd_rule AS ON UPDATE TO v1 DO INSTEAD
UPDATE t1 SET a = NEW.a WHERE a = OLD.a;
CREATE VIEW v2 WITH (security_barrier = true) AS
SELECT * FROM v1 WHERE EXISTS (SELECT 1);
EXPLAIN (COSTS OFF) UPDATE v2 SET a = 1;
DROP VIEW v2;
DROP VIEW v1;
DROP TABLE t1;
--
-- Test CREATE OR REPLACE VIEW turning a non-updatable view into an
-- auto-updatable view and adding check options in a single step