BUG #8542: Materialized View with another column_name does not work?

Started by Nonameabout 12 years ago9 messages
#1Noname
t.katsumata1122@gmail.com

The following bug has been logged on the website:

Bug reference: 8542
Logged by: Tomonari Katsumata
Email address: t.katsumata1122@gmail.com
PostgreSQL version: 9.3.1
Operating system: RHEL5 x86_64
Description:

Hi,

I'm testing the Materialized View.
When I've tried to create materialized view with specified column_name, I
got an ERROR.

example:
- Creating original table
CREATE TABLE t ( i int );

- Creating materialized view with column_name
CREATE MATERIALIZED VIEW mv_t(ii) AS SELECT * FROM t;

And then, I got a bellow ERROR.
----
ERROR: SELECT rule's target entry 1 has different column name from "ii"
----

I did not get any ERROR with non materialized view.
CREATE VIEW mv_t(ii) AS SELECT * FROM t;

Is this a bug or restriction for Materialized View?

regards,
-----------
Tomonari Katsumata

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#2Michael Paquier
michael.paquier@gmail.com
In reply to: Noname (#1)
Re: BUG #8542: Materialized View with another column_name does not work?

On Mon, Oct 21, 2013 at 6:41 PM, <t.katsumata1122@gmail.com> wrote:

- Creating materialized view with column_name
CREATE MATERIALIZED VIEW mv_t(ii) AS SELECT * FROM t;
And then, I got a bellow ERROR.
----
ERROR: SELECT rule's target entry 1 has different column name from "ii"
Is this a bug or restriction for Materialized View?

Looks like a bug as documentation is clear here: "If column names are
not provided, they are taken from the output column names of the
query".
http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html
In your case you are providing the column names, so they should be
used for this matview.

Regards,
--
Michael

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#3Tomonari Katsumata
t.katsumata1122@gmail.com
In reply to: Michael Paquier (#2)
Re: BUG #8542: Materialized View with another column_name does not work?

Hi Michael,

Thank you for replying!

I understand it seems a bug.

After some trying, I got a workaround for this.
CREATE MATERIALIZED VIEW mv_t AS SELECT i ii FROM t;

This seems working fine.
2013/10/21 Michael Paquier <michael.paquier@gmail.com>

Show quoted text

On Mon, Oct 21, 2013 at 6:41 PM, <t.katsumata1122@gmail.com> wrote:

- Creating materialized view with column_name
CREATE MATERIALIZED VIEW mv_t(ii) AS SELECT * FROM t;
And then, I got a bellow ERROR.
----
ERROR: SELECT rule's target entry 1 has different column name from "ii"
Is this a bug or restriction for Materialized View?

Looks like a bug as documentation is clear here: "If column names are
not provided, they are taken from the output column names of the
query".
http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html
In your case you are providing the column names, so they should be
used for this matview.

Regards,
--
Michael

#4Kevin Grittner
kgrittn@ymail.com
In reply to: Noname (#1)
1 attachment(s)
Re: [BUGS] BUG #8542: Materialized View with another column_name does not work?

"t.katsumata1122@gmail.com" <t.katsumata1122@gmail.com> wrote:

I'm testing the Materialized View.
When I've tried to create materialized view with specified
column_name, I got an ERROR.

example:
- Creating original table
CREATE TABLE t ( i int );

- Creating materialized view with column_name
CREATE MATERIALIZED VIEW mv_t(ii) AS SELECT * FROM t;

And then, I got a bellow ERROR.
----
ERROR:  SELECT rule's target entry 1 has different column name from "ii"
----

I did not get any ERROR with non materialized view.
CREATE VIEW mv_t(ii) AS SELECT * FROM t;

Is this a bug or restriction for Materialized View?

It's a bug.  Will fix in the next 9.3 minor release.

Moving the discussion to the -hackers list to discuss the fix.

This bug was introduced in fb60e7296c2cf15195802b4596496b179bdc905a
based on this feedback:

/messages/by-id/20600.1363022702@sss.pgh.pa.us

I picked the wrong response to that feedback.  Attached is a patch
which fixes things along the alternative lines suggested.  This
includes a regression test to ensure that this doesn't get broken
again.

If there are no objections I'll apply this within a few days.

--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

matview-column-names-fix-v1.patchtext/x-diff; name=matview-column-names-fix-v1.patchDownload
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 44,50 ****
  
  
  static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
! 					bool isSelect);
  static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
  static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
  
--- 44,50 ----
  
  
  static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
! 					bool isSelect, bool isMatview);
  static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
  static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
  
***************
*** 355,361 **** DefineQueryRewrite(char *rulename,
  		 */
  		checkRuleResultList(query->targetList,
  							RelationGetDescr(event_relation),
! 							true);
  
  		/*
  		 * ... there must not be another ON SELECT rule already ...
--- 355,363 ----
  		 */
  		checkRuleResultList(query->targetList,
  							RelationGetDescr(event_relation),
! 							true,
! 							event_relation->rd_rel->relkind ==
! 								RELKIND_MATVIEW);
  
  		/*
  		 * ... there must not be another ON SELECT rule already ...
***************
*** 484,490 **** DefineQueryRewrite(char *rulename,
  						 errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
  			checkRuleResultList(query->returningList,
  								RelationGetDescr(event_relation),
! 								false);
  		}
  	}
  
--- 486,492 ----
  						 errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
  			checkRuleResultList(query->returningList,
  								RelationGetDescr(event_relation),
! 								false, false);
  		}
  	}
  
***************
*** 615,623 **** DefineQueryRewrite(char *rulename,
   * 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.)
   */
  static void
! checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect)
  {
  	ListCell   *tllist;
  	int			i;
--- 617,628 ----
   * 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.)
+  * 
+  * We also don't enforce column name match for a materialized view.
   */
  static void
! checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect,
! 					bool isMatview)
  {
  	ListCell   *tllist;
  	int			i;
***************
*** 657,663 **** 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)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  					 errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname)));
--- 662,668 ----
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  					 errmsg("cannot convert relation containing dropped columns to view")));
  
! 		if (isSelect && !isMatview && 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)));
*** a/src/test/regress/expected/matview.out
--- b/src/test/regress/expected/matview.out
***************
*** 444,446 **** SELECT * FROM boxmv ORDER BY id;
--- 444,468 ----
  
  DROP TABLE boxes CASCADE;
  NOTICE:  drop cascades to materialized view boxmv
+ -- 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;
+ REFRESH MATERIALIZED VIEW CONCURRENTLY 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
*** a/src/test/regress/sql/matview.sql
--- b/src/test/regress/sql/matview.sql
***************
*** 167,169 **** UPDATE boxes SET b = '(2,2),(1,1)' WHERE id = 2;
--- 167,181 ----
  REFRESH MATERIALIZED VIEW CONCURRENTLY boxmv;
  SELECT * FROM boxmv ORDER BY id;
  DROP TABLE boxes 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;
+ REFRESH MATERIALIZED VIEW CONCURRENTLY mv_v;
+ SELECT * FROM v;
+ SELECT * FROM mv_v;
+ DROP TABLE v CASCADE;
#5Michael Paquier
michael.paquier@gmail.com
In reply to: Kevin Grittner (#4)
Re: [BUGS] BUG #8542: Materialized View with another column_name does not work?

On Thu, Oct 31, 2013 at 10:22 PM, Kevin Grittner <kgrittn@ymail.com> wrote:

"t.katsumata1122@gmail.com" <t.katsumata1122@gmail.com> wrote:
If there are no objections I'll apply this within a few days.

I am not sure that adding a boolean flag introducing a concept related
to matview inside checkRuleResultList is the best approach to solve
that. checkRuleResultList is something related only to rules, and has
nothing related to matviews in it yet. I have been pondering myself
about this issue, and wouldn't it be better to directly enforce
targetList in checkRuleResultList call at an upper level with the
column name list given by users if there is any instead of the list of
columns of the SELECT query?

After looking closely at the code this looks to be a better approach
from a general viewpoint. I didn't got the time to write a patch but
this is the conclusion I reached after some analysis at least...
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Ashutosh Bapat
ashutosh.bapat@enterprisedb.com
In reply to: Kevin Grittner (#4)
Re: [BUGS] BUG #8542: Materialized View with another column_name does not work?

CREATE MATERIALIZED VIEW statement ends up being CREATE TABLE AS statement
underneath with table type matview. In that case, why don't I see special
treatment only for materialized view and not CTAS in general, which allows
column names to specified like the case in the bug reported.

On Fri, Nov 1, 2013 at 3:52 AM, Kevin Grittner <kgrittn@ymail.com> wrote:

"t.katsumata1122@gmail.com" <t.katsumata1122@gmail.com> wrote:

I'm testing the Materialized View.
When I've tried to create materialized view with specified
column_name, I got an ERROR.

example:
- Creating original table
CREATE TABLE t ( i int );

- Creating materialized view with column_name
CREATE MATERIALIZED VIEW mv_t(ii) AS SELECT * FROM t;

And then, I got a bellow ERROR.
----
ERROR: SELECT rule's target entry 1 has different column name from "ii"
----

I did not get any ERROR with non materialized view.
CREATE VIEW mv_t(ii) AS SELECT * FROM t;

Is this a bug or restriction for Materialized View?

It's a bug. Will fix in the next 9.3 minor release.

Moving the discussion to the -hackers list to discuss the fix.

This bug was introduced in fb60e7296c2cf15195802b4596496b179bdc905a
based on this feedback:

/messages/by-id/20600.1363022702@sss.pgh.pa.us

I picked the wrong response to that feedback. Attached is a patch
which fixes things along the alternative lines suggested. This
includes a regression test to ensure that this doesn't get broken
again.

If there are no objections I'll apply this within a few days.

--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company

#7Kevin Grittner
kgrittn@ymail.com
In reply to: Michael Paquier (#5)
Re: [BUGS] BUG #8542: Materialized View with another column_name does not work?

Michael Paquier <michael.paquier@gmail.com> wrote:

I am not sure that adding a boolean flag introducing a concept
related to matview inside checkRuleResultList is the best
approach to solve that. checkRuleResultList is something related
only to rules, and has nothing related to matviews in it yet.

Well, I was tempted to keep that concept in DefineQueryRewrite()
where the call is made, by calling  the new bool something like
requireColumnNameMatch and not having checkRuleResultList()
continue to use isSelect for that purpose at all.
DefineQueryRewrite() already references RELKIND_RELATION once,
RELKIND_MATVIEW twice, and RELKIND_VIEW three times, so it would
hardly be introducing a new concept there.  I did it the way I did
in the posted patch because it seemed to more nearly match what Tom
was suggesting.

I have been pondering myself about this issue, and wouldn't it be
better to directly enforce targetList in checkRuleResultList call
at an upper level with the column name list given by users if
there is any instead of the list of columns of the SELECT query?

After looking closely at the code this looks to be a better
approach from a general viewpoint. I didn't got the time to write
a patch but this is the conclusion I reached after some analysis
at least...

That doesn't sound like something we could back-patch.

Anyway, I'm not sure why that would be better.  We have rewrite
rules on three kinds of relations -- tables, views, and
materialized views.  In general, a materialized view tends to
support the properties of both a view and a table, with a few
points in the rewrite code that need to pay attention to which kind
of relation the rule applies to.  Among the various validations for
the rewrite, this one validation (matching column names) does not
apply to a non-SELECT rule with a RETURNING clause or to a SELECT
rule which populates a materialized view.  If you were to move the
code to exclude this validation for the latter case, wouldn't you
also need to move the validation for the former case?  Where would
you put that?

--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Kevin Grittner
kgrittn@ymail.com
In reply to: Ashutosh Bapat (#6)
Re: [BUGS] BUG #8542: Materialized View with another column_name does not work?

Ashutosh Bapat <ashutosh.bapat@enterprisedb.com> wrote:

CREATE MATERIALIZED VIEW statement ends up being CREATE TABLE AS
statement underneath with table type matview. In that case, why
don't I see special treatment only for materialized view and not
CTAS in general, which allows column names to specified like the
case in the bug reported.

While the initial population of the matview is a lot like CTAS (and
so far shares some code), the critical difference is that CTAS
doesn't store a rule to support repopulating the table.  This issue
comes up in the validation of that rule for the matview.

--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Kevin Grittner
kgrittn@ymail.com
In reply to: Kevin Grittner (#7)
1 attachment(s)
Re: [BUGS] BUG #8542: Materialized View with another column_name does not work?

Kevin Grittner <kgrittn@ymail.com> wrote:

Michael Paquier <michael.paquier@gmail.com> wrote:

I am not sure that adding a boolean flag introducing a concept
related to matview inside checkRuleResultList is the best
approach to solve that. checkRuleResultList is something related
only to rules, and has nothing related to matviews in it yet.

Well, I was tempted to keep that concept in DefineQueryRewrite()
where the call is made, by calling  the new bool something like
requireColumnNameMatch and not having checkRuleResultList()
continue to use isSelect for that purpose at all.
DefineQueryRewrite() already references RELKIND_RELATION once,
RELKIND_MATVIEW twice, and RELKIND_VIEW three times, so it would
hardly be introducing a new concept there.

Upon reflection, that seemed to be cleaner.  Pushed fix that way.
Not much of a change from the previously posted patch, but attached
here in case anyone wants to argue against this approach.

Thanks for the report!

--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

matview-column-names-fix-v2.patchtext/x-diff; name=matview-column-names-fix-v2.patchDownload
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 44,50 ****
  
  
  static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
! 					bool isSelect);
  static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
  static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
  
--- 44,50 ----
  
  
  static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
! 					bool isSelect, bool requireColumnNameMatch);
  static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
  static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
  
***************
*** 355,361 **** DefineQueryRewrite(char *rulename,
  		 */
  		checkRuleResultList(query->targetList,
  							RelationGetDescr(event_relation),
! 							true);
  
  		/*
  		 * ... there must not be another ON SELECT rule already ...
--- 355,363 ----
  		 */
  		checkRuleResultList(query->targetList,
  							RelationGetDescr(event_relation),
! 							true,
! 							event_relation->rd_rel->relkind !=
! 								RELKIND_MATVIEW);
  
  		/*
  		 * ... there must not be another ON SELECT rule already ...
***************
*** 484,490 **** DefineQueryRewrite(char *rulename,
  						 errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
  			checkRuleResultList(query->returningList,
  								RelationGetDescr(event_relation),
! 								false);
  		}
  	}
  
--- 486,492 ----
  						 errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
  			checkRuleResultList(query->returningList,
  								RelationGetDescr(event_relation),
! 								false, false);
  		}
  	}
  
***************
*** 613,627 **** 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.)
   */
  static void
! checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect)
  {
  	ListCell   *tllist;
  	int			i;
  
  	i = 0;
  	foreach(tllist, targetList)
  	{
--- 615,634 ----
   *		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 used for choosing error messages.
!  *
!  * A SELECT targetlist may optionally require that column names match.
   */
  static void
! 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)
  	{
***************
*** 657,663 **** 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)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  					 errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname)));
--- 664,670 ----
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  					 errmsg("cannot convert relation containing dropped columns to view")));
  
! 		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)));
*** a/src/test/regress/expected/matview.out
--- b/src/test/regress/expected/matview.out
***************
*** 450,452 **** SELECT * FROM boxmv ORDER BY id;
--- 450,475 ----
  
  DROP TABLE boxes CASCADE;
  NOTICE:  drop cascades to materialized view boxmv
+ -- 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;
+ UPDATE v SET j = 3 WHERE x = 1;
+ REFRESH MATERIALIZED VIEW CONCURRENTLY mv_v;
+ SELECT * FROM v;
+  x | j 
+ ---+---
+  1 | 3
+ (1 row)
+ 
+ SELECT * FROM mv_v;
+  ii | jj 
+ ----+----
+   1 |  3
+ (1 row)
+ 
+ DROP TABLE v CASCADE;
+ NOTICE:  drop cascades to materialized view mv_v
*** a/src/test/regress/sql/matview.sql
--- b/src/test/regress/sql/matview.sql
***************
*** 173,175 **** UPDATE boxes SET b = '(2,2),(1,1)' WHERE id = 2;
--- 173,188 ----
  REFRESH MATERIALIZED VIEW CONCURRENTLY boxmv;
  SELECT * FROM boxmv ORDER BY id;
  DROP TABLE boxes 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;
+ UPDATE v SET j = 3 WHERE x = 1;
+ REFRESH MATERIALIZED VIEW CONCURRENTLY mv_v;
+ SELECT * FROM v;
+ SELECT * FROM mv_v;
+ DROP TABLE v CASCADE;