Make viewquery a copy in rewriteTargetView()
authorStephen Frost
Mon, 21 Dec 2015 15:34:28 +0000 (10:34 -0500)
committerStephen Frost
Mon, 21 Dec 2015 15:34:28 +0000 (10:34 -0500)
Rather than expect the Query returned by get_view_query() to be
read-only and then copy bits and pieces of it out, simply copy the
entire structure when we get it.  This addresses an issue where
AcquireRewriteLocks, which is called by acquireLocksOnSubLinks(),
scribbles on the parsetree passed in, which was actually an entry
in relcache, leading to segfaults with certain view definitions.
This also future-proofs us a bit for anyone adding more code to this
path.

The acquireLocksOnSubLinks() was added in commit c3e0ddd40.

Back-patch to 9.3 as that commit was.

src/backend/rewrite/rewriteHandler.c
src/test/regress/expected/updatable_views.out
src/test/regress/sql/updatable_views.sql

index d4c32e308fca2e924f1d45164b2af02bcd834616..768fb22dcf71717432460c74252f74828f0f9b37 100644 (file)
@@ -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);
index ac37ea70354dbf7be043ae2303cf77df3609612d..7b01a6bc184ec2cdda5cfb02d3fff49ac230019d 100644 (file)
@@ -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;
index c8a1c628d559ee3f9344f96bb5aabb5dfe13a181..9ae68263edbf28d10e325c875fc6835d64f7e749 100644 (file)
@@ -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;