diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d4c32e308fc..768fb22dcf7 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1893,6 +1893,9 @@ fireRules(Query *parsetree, * * Caller should have verified that the relation is a view, and therefore * we should find an ON SELECT action. + * + * Note that the pointer returned is into the relcache and therefore must + * be treated as read-only to the caller and not modified or scribbled on. */ static Query * get_view_query(Relation view) @@ -2377,8 +2380,14 @@ rewriteTargetView(Query *parsetree, Relation view) /* * If we get here, view_is_auto_updatable() has verified that the view * contains a single base relation. + * + * Get the Query from the view's ON SELECT rule. We're going to munge the + * Query to change the view's base relation into the target relation, + * along with various other changes along the way, so we need to make a + * copy of it (get_view_query() returns a pointer into the relcache, so we + * have to treat it as read-only). */ - viewquery = get_view_query(view); + viewquery = copyObject(get_view_query(view)); Assert(list_length(viewquery->jointree->fromlist) == 1); rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist); @@ -2427,7 +2436,7 @@ rewriteTargetView(Query *parsetree, Relation view) * outer query. Perhaps someday we should refactor things enough so that * we can share code with the planner.) */ - new_rte = (RangeTblEntry *) copyObject(base_rte); + new_rte = (RangeTblEntry *) base_rte; parsetree->rtable = lappend(parsetree->rtable, new_rte); new_rt_index = list_length(parsetree->rtable); @@ -2439,14 +2448,14 @@ rewriteTargetView(Query *parsetree, Relation view) new_rte->inh = false; /* - * Make a copy of the view's targetlist, adjusting its Vars to reference - * the new target RTE, ie make their varnos be new_rt_index instead of - * base_rt_index. There can be no Vars for other rels in the tlist, so - * this is sufficient to pull up the tlist expressions for use in the - * outer query. The tlist will provide the replacement expressions used - * by ReplaceVarsFromTargetList below. + * Adjust the view's targetlist Vars to reference the new target RTE, ie + * make their varnos be new_rt_index instead of base_rt_index. There can + * be no Vars for other rels in the tlist, so this is sufficient to pull + * up the tlist expressions for use in the outer query. The tlist will + * provide the replacement expressions used by ReplaceVarsFromTargetList + * below. */ - view_targetlist = copyObject(viewquery->targetList); + view_targetlist = viewquery->targetList; ChangeVarNodes((Node *) view_targetlist, base_rt_index, @@ -2581,7 +2590,7 @@ rewriteTargetView(Query *parsetree, Relation view) if (parsetree->commandType != CMD_INSERT && viewquery->jointree->quals != NULL) { - Node *viewqual = (Node *) copyObject(viewquery->jointree->quals); + Node *viewqual = (Node *) viewquery->jointree->quals; ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0); AddQual(parsetree, (Node *) viewqual); diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index ac37ea70354..7b01a6bc184 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1163,3 +1163,70 @@ DROP TABLE base_tbl_parent, base_tbl_child CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to view rw_view1 drop cascades to view rw_view2 +CREATE TABLE tx1 (a integer); +CREATE TABLE tx2 (b integer); +CREATE TABLE tx3 (c integer); +CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); +INSERT INTO vx1 values (1); +SELECT * FROM tx1; + a +--- + 1 +(1 row) + +SELECT * FROM vx1; + a +--- +(0 rows) + +DROP VIEW vx1; +DROP TABLE tx1; +DROP TABLE tx2; +DROP TABLE tx3; +CREATE TABLE tx1 (a integer); +CREATE TABLE tx2 (b integer); +CREATE TABLE tx3 (c integer); +CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); +INSERT INTO vx1 VALUES (1); +INSERT INTO vx1 VALUES (1); +SELECT * FROM tx1; + a +--- + 1 + 1 +(2 rows) + +SELECT * FROM vx1; + a +--- +(0 rows) + +DROP VIEW vx1; +DROP TABLE tx1; +DROP TABLE tx2; +DROP TABLE tx3; +CREATE TABLE tx1 (a integer, b integer); +CREATE TABLE tx2 (b integer, c integer); +CREATE TABLE tx3 (c integer, d integer); +ALTER TABLE tx1 DROP COLUMN b; +ALTER TABLE tx2 DROP COLUMN c; +ALTER TABLE tx3 DROP COLUMN d; +CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); +INSERT INTO vx1 VALUES (1); +INSERT INTO vx1 VALUES (1); +SELECT * FROM tx1; + a +--- + 1 + 1 +(2 rows) + +SELECT * FROM vx1; + a +--- +(0 rows) + +DROP VIEW vx1; +DROP TABLE tx1; +DROP TABLE tx2; +DROP TABLE tx3; diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql index c8a1c628d55..9ae68263edb 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -541,3 +541,47 @@ SELECT * FROM ONLY base_tbl_parent ORDER BY a; SELECT * FROM base_tbl_child ORDER BY a; DROP TABLE base_tbl_parent, base_tbl_child CASCADE; + +CREATE TABLE tx1 (a integer); +CREATE TABLE tx2 (b integer); +CREATE TABLE tx3 (c integer); +CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); +INSERT INTO vx1 values (1); +SELECT * FROM tx1; +SELECT * FROM vx1; + +DROP VIEW vx1; +DROP TABLE tx1; +DROP TABLE tx2; +DROP TABLE tx3; + +CREATE TABLE tx1 (a integer); +CREATE TABLE tx2 (b integer); +CREATE TABLE tx3 (c integer); +CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); +INSERT INTO vx1 VALUES (1); +INSERT INTO vx1 VALUES (1); +SELECT * FROM tx1; +SELECT * FROM vx1; + +DROP VIEW vx1; +DROP TABLE tx1; +DROP TABLE tx2; +DROP TABLE tx3; + +CREATE TABLE tx1 (a integer, b integer); +CREATE TABLE tx2 (b integer, c integer); +CREATE TABLE tx3 (c integer, d integer); +ALTER TABLE tx1 DROP COLUMN b; +ALTER TABLE tx2 DROP COLUMN c; +ALTER TABLE tx3 DROP COLUMN d; +CREATE VIEW vx1 AS SELECT a FROM tx1 WHERE EXISTS(SELECT 1 FROM tx2 JOIN tx3 ON b=c); +INSERT INTO vx1 VALUES (1); +INSERT INTO vx1 VALUES (1); +SELECT * FROM tx1; +SELECT * FROM vx1; + +DROP VIEW vx1; +DROP TABLE tx1; +DROP TABLE tx2; +DROP TABLE tx3;