From: Kevin Grittner <kgrittn@postgresql.org>
Date: Mon, 4 Nov 2013 20:45:18 +0000 (-0600)
Subject: Fix breakage of MV column name list usage.
X-Git-Tag: REL9_3_2~55
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5b6ee03a31ee695b8e57e27bb1cd82b4f0bb2ef0;p=postgresql

Fix breakage of MV column name list usage.

Per bug report from Tomonari Katsumata.

Back-patch to 9.3.
---

diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 87ec979b85..4d39724ce5 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -43,7 +43,7 @@
 
 
 static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
-					bool isSelect);
+					bool isSelect, bool requireColumnNameMatch);
 static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
 static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
 
@@ -358,7 +358,9 @@ DefineQueryRewrite(char *rulename,
 		 */
 		checkRuleResultList(query->targetList,
 							RelationGetDescr(event_relation),
-							true);
+							true,
+							event_relation->rd_rel->relkind !=
+								RELKIND_MATVIEW);
 
 		/*
 		 * ... there must not be another ON SELECT rule already ...
@@ -484,7 +486,7 @@ DefineQueryRewrite(char *rulename,
 						 errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
 			checkRuleResultList(query->returningList,
 								RelationGetDescr(event_relation),
-								false);
+								false, false);
 		}
 	}
 
@@ -616,15 +618,20 @@ DefineQueryRewrite(char *rulename,
  *		Verify that targetList produces output compatible with a tupledesc
  *
  * The targetList might be either a SELECT targetlist, or a RETURNING list;
- * isSelect tells which.  (This is mostly used for choosing error messages,
- * but also we don't enforce column name matching for RETURNING.)
+ * isSelect tells which.  This is used for choosing error messages.
+ *
+ * A SELECT targetlist may optionally require that column names match.
  */
 static void
-checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect)
+checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect,
+					bool requireColumnNameMatch)
 {
 	ListCell   *tllist;
 	int			i;
 
+	/* Only a SELECT may require a column name match. */
+	Assert(isSelect || !requireColumnNameMatch);
+
 	i = 0;
 	foreach(tllist, targetList)
 	{
@@ -660,7 +667,7 @@ checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect)
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot convert relation containing dropped columns to view")));
 
-		if (isSelect && strcmp(tle->resname, attname) != 0)
+		if (requireColumnNameMatch && strcmp(tle->resname, attname) != 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname)));
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index b0a1b48a3c..c93a17415d 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -391,3 +391,24 @@ CREATE MATERIALIZED VIEW mv2 AS SELECT * FROM mv1
   WHERE col1 = (SELECT LEAST(col1) FROM mv1) WITH NO DATA;
 DROP MATERIALIZED VIEW mv1 CASCADE;
 NOTICE:  drop cascades to materialized view mv2
+-- make sure that column names are handled correctly
+CREATE TABLE v (i int, j int);
+CREATE MATERIALIZED VIEW mv_v (ii) AS SELECT i, j AS jj FROM v;
+ALTER TABLE v RENAME COLUMN i TO x;
+INSERT INTO v values (1, 2);
+CREATE UNIQUE INDEX mv_v_ii ON mv_v (ii);
+REFRESH MATERIALIZED VIEW mv_v;
+SELECT * FROM v;
+ x | j 
+---+---
+ 1 | 2
+(1 row)
+
+SELECT * FROM mv_v;
+ ii | jj 
+----+----
+  1 |  2
+(1 row)
+
+DROP TABLE v CASCADE;
+NOTICE:  drop cascades to materialized view mv_v
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index 501cd7d6a7..b4be48e2ba 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -130,3 +130,14 @@ CREATE MATERIALIZED VIEW mv1 AS SELECT 1 AS col1 WITH NO DATA;
 CREATE MATERIALIZED VIEW mv2 AS SELECT * FROM mv1
   WHERE col1 = (SELECT LEAST(col1) FROM mv1) WITH NO DATA;
 DROP MATERIALIZED VIEW mv1 CASCADE;
+
+-- make sure that column names are handled correctly
+CREATE TABLE v (i int, j int);
+CREATE MATERIALIZED VIEW mv_v (ii) AS SELECT i, j AS jj FROM v;
+ALTER TABLE v RENAME COLUMN i TO x;
+INSERT INTO v values (1, 2);
+CREATE UNIQUE INDEX mv_v_ii ON mv_v (ii);
+REFRESH MATERIALIZED VIEW mv_v;
+SELECT * FROM v;
+SELECT * FROM mv_v;
+DROP TABLE v CASCADE;