From 0e952cdb9029cf6f8a574f4d888cbb134e646d0e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 21 Dec 2000 17:36:15 +0000
Subject: [PATCH] Fix longstanding bug with VIEW using BETWEEN: OffsetVarNodes
 would get applied to the duplicated subtree twice.  Probably someday we
 should fix the parser not to generate multiple links to the same subtree, but
 for now a quick copyObject() is the path of least resistance.

---
 src/backend/commands/view.c | 57 +++++++++++++++++++------------------
 1 file changed, 29 insertions(+), 28 deletions(-)

diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 2262d4bd508..0f726e8e6e0 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: view.c,v 1.50 2000/10/26 17:04:12 tgl Exp $
+ *	$Id: view.c,v 1.51 2000/12/21 17:36:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -156,12 +156,12 @@ FormViewRetrieveRule(char *viewName, Query *viewParse)
 static void
 DefineViewRules(char *viewName, Query *viewParse)
 {
-	RuleStmt   *retrieve_rule = NULL;
+	RuleStmt   *retrieve_rule;
 
 #ifdef NOTYET
-	RuleStmt   *replace_rule = NULL;
-	RuleStmt   *append_rule = NULL;
-	RuleStmt   *delete_rule = NULL;
+	RuleStmt   *replace_rule;
+	RuleStmt   *append_rule;
+	RuleStmt   *delete_rule;
 
 #endif
 
@@ -198,16 +198,11 @@ DefineViewRules(char *viewName, Query *viewParse)
  * Of course we must also increase the 'varnos' of all the Var nodes
  * by 2...
  *
- * These extra RT entries are not actually used in the query, obviously.
- * We add them so that views look the same as ON SELECT rules ---
- * the rule rewriter assumes that ALL rules have OLD and NEW RTEs.
- *
- * NOTE: these are destructive changes. It would be difficult to
- * make a complete copy of the parse tree and make the changes
- * in the copy.
+ * These extra RT entries are not actually used in the query,
+ * except for run-time permission checking.
  *---------------------------------------------------------------
  */
-static void
+static Query *
 UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
 {
 	List	   *new_rt;
@@ -215,7 +210,17 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
 			   *rt_entry2;
 
 	/*
-	 * create the 2 new range table entries and form the new range
+	 * Make a copy of the given parsetree.  It's not so much that we
+	 * don't want to scribble on our input, it's that the parser has
+	 * a bad habit of outputting multiple links to the same subtree
+	 * for constructs like BETWEEN, and we mustn't have OffsetVarNodes
+	 * increment the varno of a Var node twice.  copyObject will expand
+	 * any multiply-referenced subtree into multiple copies.
+	 */
+	viewParse = (Query *) copyObject(viewParse);
+
+	/*
+	 * Create the 2 new range table entries and form the new range
 	 * table... OLD first, then NEW....
 	 */
 	rt_entry1 = addRangeTableEntry(NULL, viewName,
@@ -230,16 +235,14 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
 
 	new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
 
-	/*
-	 * Now the tricky part.... Update the range table in place... Be
-	 * careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
-	 */
 	viewParse->rtable = new_rt;
 
 	/*
-	 * now offset all var nodes by 2, and jointree RT indexes too.
+	 * Now offset all var nodes by 2, and jointree RT indexes too.
 	 */
 	OffsetVarNodes((Node *) viewParse, 2, 0);
+
+	return viewParse;
 }
 
 /*-------------------------------------------------------------------
@@ -257,15 +260,11 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
 void
 DefineView(char *viewName, Query *viewParse)
 {
-	List	   *viewTlist;
-
-	viewTlist = viewParse->targetList;
-
 	/*
 	 * Create the "view" relation NOTE: if it already exists, the xact
 	 * will be aborted.
 	 */
-	DefineVirtualRelation(viewName, viewTlist);
+	DefineVirtualRelation(viewName, viewParse->targetList);
 
 	/*
 	 * The relation we have just created is not visible to any other
@@ -276,11 +275,13 @@ DefineView(char *viewName, Query *viewParse)
 
 	/*
 	 * The range table of 'viewParse' does not contain entries for the
-	 * "OLD" and "NEW" relations. So... add them! NOTE: we make the
-	 * update in place! After this call 'viewParse' will never be what it
-	 * used to be...
+	 * "OLD" and "NEW" relations. So... add them!
+	 */
+	viewParse = UpdateRangeTableOfViewParse(viewName, viewParse);
+
+	/*
+	 * Now create the rules associated with the view.
 	 */
-	UpdateRangeTableOfViewParse(viewName, viewParse);
 	DefineViewRules(viewName, viewParse);
 }
 
-- 
GitLab