diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 903cf59f9d..26c9e348d5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -2026,6 +2026,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. */ Query * get_view_query(Relation view) @@ -2613,9 +2616,16 @@ rewriteTargetView(Query *parsetree, Relation view) List *view_targetlist; ListCell *lc; - /* The view must be updatable, else fail */ - viewquery = get_view_query(view); + /* + * 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 = copyObject(get_view_query(view)); + /* The view must be updatable, else fail */ auto_update_detail = view_query_is_auto_updatable(viewquery, parsetree->commandType != CMD_DELETE); @@ -2779,7 +2789,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); @@ -2791,14 +2801,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, @@ -2949,7 +2959,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); @@ -3028,7 +3038,7 @@ rewriteTargetView(Query *parsetree, Relation view) if (viewquery->jointree->quals != NULL) { - wco->qual = (Node *) copyObject(viewquery->jointree->quals); + wco->qual = (Node *) viewquery->jointree->quals; ChangeVarNodes(wco->qual, base_rt_index, new_rt_index, 0); /* diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 8e5463a28d..6c7137155d 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2353,3 +2353,70 @@ DROP TABLE t1, t11, t12, t111 CASCADE; NOTICE: drop cascades to view v1 DROP FUNCTION snoop(anyelement); DROP FUNCTION leakproof(anyelement); +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 8fe96f5c51..5297a71822 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -1020,3 +1020,47 @@ TABLE t1; -- verify all a<=5 are intact DROP TABLE t1, t11, t12, t111 CASCADE; DROP FUNCTION snoop(anyelement); DROP FUNCTION leakproof(anyelement); + +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;