mv_v1.1.patch

text/x-patch

Filename: mv_v1.1.patch
Type: text/x-patch
Part: 0
Message: - GSoC - snapshot materialized view (work-in-progress) patch

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
Series: patch v1
File+
doc/src/sgml/catalogs.sgml 2 2
doc/src/sgml/ref/alter_view.sgml 13 3
doc/src/sgml/ref/comment.sgml 2 1
doc/src/sgml/ref/create_view.sgml 13 1
src/backend/access/common/reloptions.c 2 0
src/backend/access/heap/heapam.c 3 2
src/backend/catalog/dependency.c 4 0
src/backend/catalog/heap.c 4 1
src/backend/catalog/system_views.sql 1 1
src/backend/commands/alter.c 3 0
src/backend/commands/comment.c 9 0
src/backend/commands/copy.c 2 2
src/backend/commands/indexcmds.c 1 0
src/backend/commands/tablecmds.c 44 2
src/backend/commands/view.c 15 7
src/backend/executor/execMain.c 107 57
src/backend/nodes/copyfuncs.c 2 0
src/backend/nodes/equalfuncs.c 2 0
src/backend/nodes/outfuncs.c 1 0
src/backend/parser/analyze.c 143 0
src/backend/parser/gram.y 57 13
src/backend/rewrite/rewriteDefine.c 7 6
src/backend/rewrite/rewriteHandler.c 45 1
src/backend/rewrite/rewriteSupport.c 3 1
src/backend/tcop/utility.c 18 1
src/backend/utils/adt/dbsize.c 2 0
src/backend/utils/adt/ruleutils.c 4 2
src/bin/pg_dump/pg_backup_archiver.c 3 1
src/bin/pg_dump/pg_dump.c 64 4
src/bin/pg_dump/pg_dump_sort.c 36 1
src/bin/psql/command.c 2 1
src/bin/psql/describe.c 30 11
src/bin/psql/help.c 2 1
src/include/catalog/pg_class.h 1 0
src/include/nodes/nodes.h 1 0
src/include/nodes/parsenodes.h 4 1
src/include/nodes/primnodes.h 1 0
src/include/parser/kwlist.h 2 0
*** ./doc/src/sgml/catalogs.sgml.orig	2010-07-06 10:43:16.000000000 +0200
--- ./doc/src/sgml/catalogs.sgml	2010-07-08 15:22:14.000000000 +0200
***************
*** 1559,1565 ****
        <entry></entry>
        <entry>
         <literal>r</> = ordinary table, <literal>i</> = index,
!        <literal>S</> = sequence, <literal>v</> = view, <literal>c</> =
         composite type, <literal>t</> = TOAST
         table
        </entry>
--- 1559,1565 ----
        <entry></entry>
        <entry>
         <literal>r</> = ordinary table, <literal>i</> = index,
!        <literal>S</> = sequence, <literal>v</> = view, <literal>m</> = materialized view, <literal>c</> =
         composite type, <literal>t</> = TOAST
         table
        </entry>
***************
*** 4086,4092 ****
        <entry>
         Event type that the rule is for: 1 = <command>SELECT</>, 2 =
         <command>UPDATE</>, 3 = <command>INSERT</>, 4 =
!        <command>DELETE</>
        </entry>
       </row>
  
--- 4086,4092 ----
        <entry>
         Event type that the rule is for: 1 = <command>SELECT</>, 2 =
         <command>UPDATE</>, 3 = <command>INSERT</>, 4 =
!        <command>DELETE</>, 6 = <command>REFRESH</>
        </entry>
       </row>
  
*** ./doc/src/sgml/ref/alter_view.sgml.orig	2010-07-06 10:43:16.000000000 +0200
--- ./doc/src/sgml/ref/alter_view.sgml	2010-07-08 15:22:14.000000000 +0200
***************
*** 23,31 ****
  <synopsis>
  ALTER VIEW <replaceable class="parameter">name</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET DEFAULT <replaceable class="PARAMETER">expression</replaceable>
  ALTER VIEW <replaceable class="parameter">name</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
! ALTER VIEW <replaceable class="parameter">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
! ALTER VIEW <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
! ALTER VIEW <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
  </synopsis>
   </refsynopsisdiv>
    
--- 23,32 ----
  <synopsis>
  ALTER VIEW <replaceable class="parameter">name</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET DEFAULT <replaceable class="PARAMETER">expression</replaceable>
  ALTER VIEW <replaceable class="parameter">name</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
! ALTER [ MATERIALIZED ] VIEW <replaceable class="parameter">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
! ALTER [ MATERIALIZED ] VIEW <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
! ALTER [ MATERIALIZED ] VIEW <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
! ALTER MATERIALIZED VIEW <replaceable class="parameter">name</replaceable> REFRESH
  </synopsis>
   </refsynopsisdiv>
    
***************
*** 102,107 ****
--- 103,117 ----
       </para>
      </listitem>
     </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>REFRESH</literal></term>
+     <listitem>
+      <para>
+       Refresh data in the materialized view by rebuilding definition.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </refsect1>
  
*** ./doc/src/sgml/ref/comment.sgml.orig	2010-07-06 10:43:16.000000000 +0200
--- ./doc/src/sgml/ref/comment.sgml	2010-07-08 15:22:14.000000000 +0200
***************
*** 49,55 ****
    TEXT SEARCH TEMPLATE <replaceable class="PARAMETER">object_name</replaceable> |
    TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
    TYPE <replaceable class="PARAMETER">object_name</replaceable> |
!   VIEW <replaceable class="PARAMETER">object_name</replaceable>
  } IS '<replaceable class="PARAMETER">text</replaceable>'
  </synopsis>
   </refsynopsisdiv>
--- 49,55 ----
    TEXT SEARCH TEMPLATE <replaceable class="PARAMETER">object_name</replaceable> |
    TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
    TYPE <replaceable class="PARAMETER">object_name</replaceable> |
!   [ MATERIALIZED ] VIEW <replaceable class="PARAMETER">object_name</replaceable>
  } IS '<replaceable class="PARAMETER">text</replaceable>'
  </synopsis>
   </refsynopsisdiv>
***************
*** 268,273 ****
--- 268,274 ----
  COMMENT ON TRIGGER my_trigger ON my_table IS 'Used for RI';
  COMMENT ON TYPE complex IS 'Complex number data type';
  COMMENT ON VIEW my_view IS 'View of departmental costs';
+ COMMENT ON MATERIALIZED VIEW salaries_mv IS 'Materialized View of salaries according to the profession';
  </programlisting>
    </para>
   </refsect1>
*** ./doc/src/sgml/ref/create_view.sgml.orig	2010-07-06 20:32:12.000000000 +0200
--- ./doc/src/sgml/ref/create_view.sgml	2010-07-08 15:22:14.000000000 +0200
***************
*** 21,27 ****
  
   <refsynopsisdiv>
  <synopsis>
! CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
      AS <replaceable class="PARAMETER">query</replaceable>
  </synopsis>
   </refsynopsisdiv>
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
! CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ MATERIALIZED ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
      AS <replaceable class="PARAMETER">query</replaceable>
  </synopsis>
   </refsynopsisdiv>
***************
*** 80,85 ****
--- 80,97 ----
     </varlistentry>
  
     <varlistentry>
+     <term><literal>MATERIALIZED</></term>
+     <listitem>
+      <para>
+       If specified, view is created materialized. The result of 
+       definition is stored in physical table. Materialized view
+       is created empty, so to fill materialized view with data
+       run separate refresh command <xref linkend="sql-alterview">.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="parameter">name</replaceable></term>
      <listitem>
       <para>
*** ./src/backend/access/common/reloptions.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/access/common/reloptions.c	2010-07-08 15:22:14.000000000 +0200
***************
*** 775,780 ****
--- 775,781 ----
  	switch (classForm->relkind)
  	{
  		case RELKIND_RELATION:
+ 		case RELKIND_MATVIEW:
  		case RELKIND_TOASTVALUE:
  		case RELKIND_UNCATALOGED:
  			options = heap_reloptions(classForm->relkind, datum, false);
***************
*** 1172,1177 ****
--- 1173,1179 ----
  			}
  			return (bytea *) rdopts;
  		case RELKIND_RELATION:
+ 		case RELKIND_MATVIEW:
  			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
  		default:
  			/* sequences, composite types and views are not supported */
*** ./src/backend/access/heap/heapam.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/access/heap/heapam.c	2010-07-08 20:42:56.000000000 +0200
***************
*** 1877,1883 ****
  	 * Note: below this point, heaptup is the data we actually intend to store
  	 * into the relation; tup is the caller's original untoasted data.
  	 */
! 	if (relation->rd_rel->relkind != RELKIND_RELATION)
  	{
  		/* toast table entries should never be recursively toasted */
  		Assert(!HeapTupleHasExternal(tup));
--- 1877,1884 ----
  	 * Note: below this point, heaptup is the data we actually intend to store
  	 * into the relation; tup is the caller's original untoasted data.
  	 */
! 	if (relation->rd_rel->relkind != RELKIND_RELATION &&
! 		relation->rd_rel->relkind != RELKIND_MATVIEW)
  	{
  		/* toast table entries should never be recursively toasted */
  		Assert(!HeapTupleHasExternal(tup));
***************
*** 4126,4132 ****
  	/*
  	 * We're about to remove tuples. In Hot Standby mode, ensure that there's
  	 * no queries running for which the removed tuples are still visible.
! 	 *
  	 * Not all HEAP2_CLEAN records remove tuples with xids, so we only want to
  	 * conflict on the records that cause MVCC failures for user queries. If
  	 * latestRemovedXid is invalid, skip conflict processing.
--- 4127,4133 ----
  	/*
  	 * We're about to remove tuples. In Hot Standby mode, ensure that there's
  	 * no queries running for which the removed tuples are still visible.
! 	 * 
  	 * Not all HEAP2_CLEAN records remove tuples with xids, so we only want to
  	 * conflict on the records that cause MVCC failures for user queries. If
  	 * latestRemovedXid is invalid, skip conflict processing.
*** ./src/backend/catalog/dependency.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/catalog/dependency.c	2010-07-08 15:22:14.000000000 +0200
***************
*** 2731,2736 ****
--- 2731,2740 ----
  			appendStringInfo(buffer, _("view %s"),
  							 relname);
  			break;
+ 		case RELKIND_MATVIEW:
+ 			appendStringInfo(buffer, _("materialized view %s"),
+ 							 relname);
+ 			break;
  		case RELKIND_COMPOSITE_TYPE:
  			appendStringInfo(buffer, _("composite type %s"),
  							 relname);
*** ./src/backend/catalog/heap.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/catalog/heap.c	2010-07-08 15:22:14.000000000 +0200
***************
*** 758,763 ****
--- 758,764 ----
  		case RELKIND_RELATION:
  		case RELKIND_INDEX:
  		case RELKIND_TOASTVALUE:
+ 		case RELKIND_MATVIEW:
  			/* The relation is real, but as yet empty */
  			new_rel_reltup->relpages = 0;
  			new_rel_reltup->reltuples = 0;
***************
*** 776,782 ****
  
  	/* Initialize relfrozenxid */
  	if (relkind == RELKIND_RELATION ||
! 		relkind == RELKIND_TOASTVALUE)
  	{
  		/*
  		 * Initialize to the minimum XID that could put tuples in the table.
--- 777,784 ----
  
  	/* Initialize relfrozenxid */
  	if (relkind == RELKIND_RELATION ||
! 		relkind == RELKIND_TOASTVALUE ||
! 		relkind == RELKIND_MATVIEW)
  	{
  		/*
  		 * Initialize to the minimum XID that could put tuples in the table.
***************
*** 1027,1032 ****
--- 1029,1035 ----
  	 */
  	if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
  							  relkind == RELKIND_VIEW ||
+ 							  relkind == RELKIND_MATVIEW ||
  							  relkind == RELKIND_COMPOSITE_TYPE))
  		new_array_oid = AssignTypeArrayOid();
  
*** ./src/backend/catalog/system_views.sql.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/catalog/system_views.sql	2010-07-08 15:22:14.000000000 +0200
***************
*** 76,82 ****
          pg_get_userbyid(C.relowner) AS viewowner, 
          pg_get_viewdef(C.oid) AS definition 
      FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) 
!     WHERE C.relkind = 'v';
  
  CREATE VIEW pg_tables AS 
      SELECT 
--- 76,82 ----
          pg_get_userbyid(C.relowner) AS viewowner, 
          pg_get_viewdef(C.oid) AS definition 
      FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) 
!     WHERE C.relkind = 'v' OR C.relkind = 'm';
  
  CREATE VIEW pg_tables AS 
      SELECT 
*** ./src/backend/commands/alter.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/commands/alter.c	2010-07-08 15:22:14.000000000 +0200
***************
*** 87,92 ****
--- 87,93 ----
  		case OBJECT_TABLE:
  		case OBJECT_SEQUENCE:
  		case OBJECT_VIEW:
+ 		case OBJECT_MATVIEW:
  		case OBJECT_INDEX:
  		case OBJECT_COLUMN:
  		case OBJECT_TRIGGER:
***************
*** 102,107 ****
--- 103,109 ----
  					case OBJECT_TABLE:
  					case OBJECT_SEQUENCE:
  					case OBJECT_VIEW:
+ 					case OBJECT_MATVIEW:
  					case OBJECT_INDEX:
  						{
  							/*
***************
*** 188,193 ****
--- 190,196 ----
  		case OBJECT_SEQUENCE:
  		case OBJECT_TABLE:
  		case OBJECT_VIEW:
+ 		case OBJECT_MATVIEW:
  			CheckRelationOwnership(stmt->relation, true);
  			AlterTableNamespace(stmt->relation, stmt->newschema,
  								stmt->objectType);
*** ./src/backend/commands/comment.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/commands/comment.c	2010-07-08 15:22:14.000000000 +0200
***************
*** 107,112 ****
--- 107,113 ----
  		case OBJECT_SEQUENCE:
  		case OBJECT_TABLE:
  		case OBJECT_VIEW:
+ 		case OBJECT_MATVIEW:
  			CommentRelation(stmt->objtype, stmt->objname, stmt->comment);
  			break;
  		case OBJECT_COLUMN:
***************
*** 580,585 ****
--- 581,593 ----
  						 errmsg("\"%s\" is not a view",
  								RelationGetRelationName(relation))));
  			break;
+ 		case OBJECT_MATVIEW:
+ 			if (relation->rd_rel->relkind != RELKIND_MATVIEW)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 						 errmsg("\"%s\" is not a materialized view",
+ 								RelationGetRelationName(relation))));
+ 			break;
  	}
  
  	/* Create the comment using the relation's oid */
***************
*** 636,641 ****
--- 644,650 ----
  	 */
  	if (relation->rd_rel->relkind != RELKIND_RELATION &&
  		relation->rd_rel->relkind != RELKIND_VIEW &&
+ 		relation->rd_rel->relkind != RELKIND_MATVIEW &&
  		relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
*** ./src/backend/commands/copy.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/commands/copy.c	2010-07-08 15:22:14.000000000 +0200
***************
*** 1227,1233 ****
  
  	if (cstate->rel)
  	{
! 		if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
  		{
  			if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
  				ereport(ERROR,
--- 1227,1233 ----
  
  	if (cstate->rel)
  	{
! 		if (cstate->rel->rd_rel->relkind != RELKIND_RELATION && cstate->rel->rd_rel->relkind != RELKIND_MATVIEW)
  		{
  			if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
  				ereport(ERROR,
***************
*** 1701,1707 ****
  
  	Assert(cstate->rel);
  
! 	if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
  	{
  		if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
  			ereport(ERROR,
--- 1701,1707 ----
  
  	Assert(cstate->rel);
  
! 	if (cstate->rel->rd_rel->relkind != RELKIND_RELATION && cstate->rel->rd_rel->relkind != RELKIND_MATVIEW)
  	{
  		if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
  			ereport(ERROR,
*** ./src/backend/commands/indexcmds.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/commands/indexcmds.c	2010-07-08 20:48:53.000000000 +0200
***************
*** 181,186 ****
--- 181,187 ----
  
  	/* Note: during bootstrap may see uncataloged relation */
  	if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ 		rel->rd_rel->relkind != RELKIND_MATVIEW &&
  		rel->rd_rel->relkind != RELKIND_UNCATALOGED)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
*** ./src/backend/commands/tablecmds.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/commands/tablecmds.c	2010-07-08 20:52:26.000000000 +0200
***************
*** 205,210 ****
--- 205,216 ----
  		gettext_noop("view \"%s\" does not exist, skipping"),
  		gettext_noop("\"%s\" is not a view"),
  	gettext_noop("Use DROP VIEW to remove a view.")},
+ 	{RELKIND_MATVIEW,
+ 		ERRCODE_UNDEFINED_TABLE,
+ 		gettext_noop("materialized view \"%s\" does not exist"),
+ 		gettext_noop("materialized view \"%s\" does not exist, skipping"),
+ 		gettext_noop("\"%s\" is not a materialized view"),
+ 	gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
  	{RELKIND_INDEX,
  		ERRCODE_UNDEFINED_OBJECT,
  		gettext_noop("index \"%s\" does not exist"),
***************
*** 678,683 ****
--- 684,693 ----
  			relkind = RELKIND_VIEW;
  			break;
  
+ 		case OBJECT_MATVIEW:
+ 			relkind = RELKIND_MATVIEW;
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized drop object type: %d",
  				 (int) drop->removeType);
***************
*** 1970,1977 ****
  		relkind != RELKIND_INDEX)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 			   errmsg("\"%s\" is not a table, view, composite type or index",
! 					  RelationGetRelationName(targetrelation))));
  
  	/*
  	 * permissions checking.  only the owner of a class can change its schema.
--- 1980,1987 ----
  		relkind != RELKIND_INDEX)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 				 errmsg("\"%s\" is not a table, view, composite type or index",
! 						RelationGetRelationName(targetrelation))));
  
  	/*
  	 * permissions checking.  only the owner of a class can change its schema.
***************
*** 2358,2363 ****
--- 2368,2381 ----
  								RelationGetRelationName(rel))));
  			break;
  
+ 		case OBJECT_MATVIEW:
+ 			if (rel->rd_rel->relkind != RELKIND_MATVIEW)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 						 errmsg("\"%s\" is not a materialized view",
+ 								RelationGetRelationName(rel))));
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized object type: %d", (int) stmt->relkind);
  	}
***************
*** 3321,3326 ****
--- 3339,3350 ----
  {
  	if (rel->rd_rel->relkind != RELKIND_RELATION)
  	{
+ 		if (rel->rd_rel->relkind == RELKIND_MATVIEW)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("Materialized view \"%s\" cannot be altered",
+ 							RelationGetRelationName(rel))));
+ 
  		if (allowView)
  		{
  			if (rel->rd_rel->relkind != RELKIND_VIEW)
***************
*** 3359,3364 ****
--- 3383,3389 ----
  ATSimplePermissionsRelationOrIndex(Relation rel)
  {
  	if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ 		rel->rd_rel->relkind != RELKIND_MATVIEW &&
  		rel->rd_rel->relkind != RELKIND_INDEX)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
***************
*** 4111,4116 ****
--- 4136,4142 ----
  	 * allowSystemTableMods to be turned on.
  	 */
  	if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ 		rel->rd_rel->relkind != RELKIND_MATVIEW &&
  		rel->rd_rel->relkind != RELKIND_INDEX)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
***************
*** 6439,6444 ****
--- 6465,6471 ----
  	{
  		case RELKIND_RELATION:
  		case RELKIND_VIEW:
+ 		case RELKIND_MATVIEW:
  			/* ok to change owner */
  			break;
  		case RELKIND_INDEX:
***************
*** 7741,7746 ****
--- 7768,7781 ----
  								RelationGetRelationName(rel))));
  			break;
  
+ 		case OBJECT_MATVIEW:
+ 			if (rel->rd_rel->relkind != RELKIND_MATVIEW)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 						 errmsg("\"%s\" is not a materialized view",
+ 								RelationGetRelationName(rel))));
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized object type: %d", (int) stmttype);
  	}
***************
*** 7750,7755 ****
--- 7785,7791 ----
  	{
  		case RELKIND_RELATION:
  		case RELKIND_VIEW:
+ 		case RELKIND_MATVIEW:
  			/* ok to change schema */
  			break;
  		case RELKIND_SEQUENCE:
***************
*** 7822,7827 ****
--- 7858,7869 ----
  		AlterConstraintNamespaces(relid, oldNspOid, nspOid, false);
  	}
  
+ 	/* Fix other dependent stuff with materialized view */
+ 	if (rel->rd_rel->relkind == RELKIND_MATVIEW)
+ 	{
+ 		AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid);
+ 	}
+ 
  	heap_close(classRel, RowExclusiveLock);
  
  	/* close rel, but keep lock until commit */
*** ./src/backend/commands/view.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/commands/view.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 97,103 ****
   *---------------------------------------------------------------------
   */
  static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
  {
  	Oid			viewOid,
  				namespaceId;
--- 97,103 ----
   *---------------------------------------------------------------------
   */
  static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace, char relkind)
  {
  	Oid			viewOid,
  				namespaceId;
***************
*** 155,160 ****
--- 155,168 ----
  		rel = relation_open(viewOid, AccessExclusiveLock);
  
  		/*
+ 		 * Check if do not try to replace materialized view.
+ 		 */
+ 		if (rel->rd_rel->relkind == RELKIND_MATVIEW)
+ 			ereport(ERROR,
+                           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                   errmsg("CREATE OR REPLACE on materialized view is not supported!"),
+                                   errhint("Use CREATE MATERIALIZED VIEW ...")));
+ 		/*
  		 * Make sure it *is* a view, and do permissions checks.
  		 */
  		if (rel->rd_rel->relkind != RELKIND_VIEW)
***************
*** 239,245 ****
  		 * existing view, so we don't need more code to complain if "replace"
  		 * is false).
  		 */
! 		return DefineRelation(createStmt, RELKIND_VIEW);
  	}
  }
  
--- 247,253 ----
  		 * existing view, so we don't need more code to complain if "replace"
  		 * is false).
  		 */
! 		return DefineRelation(createStmt, relkind);
  	}
  }
  
***************
*** 299,305 ****
  }
  
  static void
! DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
  {
  	/*
  	 * Set up the ON SELECT rule.  Since the query has already been through
--- 307,313 ----
  }
  
  static void
! DefineViewRules(Oid viewOid, Query *viewParse, bool is_instead, bool replace, bool is_materialized)
  {
  	/*
  	 * Set up the ON SELECT rule.  Since the query has already been through
***************
*** 308,315 ****
  	DefineQueryRewrite(pstrdup(ViewSelectRuleName),
  					   viewOid,
  					   NULL,
! 					   CMD_SELECT,
! 					   true,
  					   replace,
  					   list_make1(viewParse));
  
--- 316,323 ----
  	DefineQueryRewrite(pstrdup(ViewSelectRuleName),
  					   viewOid,
  					   NULL,
! 					   is_materialized ? CMD_REFRESH : CMD_SELECT,
! 					   is_materialized ? false : is_instead,
  					   replace,
  					   list_make1(viewParse));
  
***************
*** 465,471 ****
  	 * aborted.
  	 */
  	viewOid = DefineVirtualRelation(view, viewParse->targetList,
! 									stmt->replace);
  
  	/*
  	 * The relation we have just created is not visible to any other commands
--- 473,479 ----
  	 * aborted.
  	 */
  	viewOid = DefineVirtualRelation(view, viewParse->targetList,
! 										stmt->replace, (stmt->ismaterialized ? RELKIND_MATVIEW : RELKIND_VIEW));
  
  	/*
  	 * The relation we have just created is not visible to any other commands
***************
*** 483,487 ****
  	/*
  	 * Now create the rules associated with the view.
  	 */
! 	DefineViewRules(viewOid, viewParse, stmt->replace);
  }
--- 491,495 ----
  	/*
  	 * Now create the rules associated with the view.
  	 */
! 	DefineViewRules(viewOid, viewParse, true, stmt->replace, stmt->ismaterialized);
  }
*** ./src/backend/executor/execMain.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/executor/execMain.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 39,49 ****
--- 39,52 ----
  #include "catalog/heap.h"
  #include "catalog/namespace.h"
  #include "catalog/toasting.h"
+ #include "catalog/dependency.h"
  #include "commands/tablespace.h"
  #include "commands/trigger.h"
+ #include "commands/cluster.h"
  #include "executor/execdebug.h"
  #include "executor/instrument.h"
  #include "miscadmin.h"
+ #include "nodes/nodeFuncs.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_clause.h"
  #include "parser/parsetree.h"
***************
*** 56,61 ****
--- 59,65 ----
  #include "utils/memutils.h"
  #include "utils/snapmgr.h"
  #include "utils/tqual.h"
+ #include "lib/stringinfo.h"
  
  
  /* Hooks for plugins to get control in ExecutorStart/Run/End() */
***************
*** 868,873 ****
--- 872,878 ----
  	switch (resultRelationDesc->rd_rel->relkind)
  	{
  		case RELKIND_RELATION:
+ 		case RELKIND_MATVIEW:
  			/* OK */
  			break;
  		case RELKIND_SEQUENCE:
***************
*** 2073,2078 ****
--- 2078,2085 ----
  	Oid			intoRelationId;
  	TupleDesc	tupdesc;
  	DR_intorel *myState;
+ 	Oid			tableOid;
+ 	Oid			OIDNewHeap;
  	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
  
  	Assert(into);
***************
*** 2144,2209 ****
  						   get_tablespace_name(tablespaceId));
  	}
  
! 	/* Parse and validate any reloptions */
! 	reloptions = transformRelOptions((Datum) 0,
! 									 into->options,
! 									 NULL,
! 									 validnsps,
! 									 true,
! 									 false);
! 	(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
! 
! 	/* Copy the tupdesc because heap_create_with_catalog modifies it */
! 	tupdesc = CreateTupleDescCopy(queryDesc->tupDesc);
! 
! 	/* Now we can actually create the new relation */
! 	intoRelationId = heap_create_with_catalog(intoName,
! 											  namespaceId,
! 											  tablespaceId,
! 											  InvalidOid,
! 											  InvalidOid,
! 											  InvalidOid,
! 											  GetUserId(),
! 											  tupdesc,
! 											  NIL,
! 											  RELKIND_RELATION,
! 											  false,
! 											  false,
! 											  true,
! 											  0,
! 											  into->onCommit,
! 											  reloptions,
! 											  true,
! 											  allowSystemTableMods);
! 
! 	FreeTupleDesc(tupdesc);
! 
! 	/*
! 	 * Advance command counter so that the newly-created relation's catalog
! 	 * tuples will be visible to heap_open.
! 	 */
! 	CommandCounterIncrement();
! 
! 	/*
! 	 * If necessary, create a TOAST table for the INTO relation. Note that
! 	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
! 	 * the TOAST table will be visible for insertion.
! 	 */
! 	reloptions = transformRelOptions((Datum) 0,
! 									 into->options,
! 									 "toast",
! 									 validnsps,
! 									 true,
! 									 false);
  
! 	(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
  
! 	AlterTableCreateToastTable(intoRelationId, reloptions);
  
- 	/*
- 	 * And open the constructed table for writing.
- 	 */
- 	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
  
  	/*
  	 * Now replace the query's DestReceiver with one for SELECT INTO
--- 2151,2240 ----
  						   get_tablespace_name(tablespaceId));
  	}
  
! 	/*
! 	 * There are two types of SELECT INTO stmt. One for refreshing data 
! 	 * in materialized views. The second one for regular SELECT INTO stmt.
! 	 */
! 	if (into->isrefresh)
! 	{
! 		/* Open the constructed table for copying */
! 		intoRelationDesc = heap_openrv(into->rel, AccessExclusiveLock);
  
! 		tableOid = RelationGetRelid(intoRelationDesc);
  
! 		/* Close relcache entry */
! 		heap_close(intoRelationDesc, NoLock);
! 
! 		/* Create new heap, target of REFRESH command */
! 		OIDNewHeap = make_new_heap(tableOid, tablespaceId);
! 		
! 		/* And open the constructed table for writing */
! 		intoRelationDesc = heap_open(OIDNewHeap, AccessExclusiveLock);
! 	}
! 	else
! 	{
! 		/* Parse and validate any reloptions */
! 		reloptions = transformRelOptions((Datum) 0,
! 										 into->options,
! 										 NULL,
! 										 validnsps,
! 										 true,
! 										 false);
! 		(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
! 
! 		/* Copy the tupdesc because heap_create_with_catalog modifies it */
! 		tupdesc = CreateTupleDescCopy(queryDesc->tupDesc);
! 
! 		/* Now we can actually create the new relation */
! 		intoRelationId = heap_create_with_catalog(intoName,
! 												  namespaceId,
! 												  tablespaceId,
! 												  InvalidOid,
! 												  InvalidOid,
! 												  InvalidOid,
! 												  GetUserId(),
! 												  tupdesc,
! 												  NIL,
! 												  RELKIND_RELATION,
! 												  false,
! 												  false,
! 												  true,
! 												  0,
! 												  into->onCommit,
! 												  reloptions,
! 												  true,
! 												  allowSystemTableMods);
! 
! 		FreeTupleDesc(tupdesc);
! 
! 		/*
! 		 * Advance command counter so that the newly-created relation's catalog
! 		 * tuples will be visible to heap_open.
! 		 */
! 		CommandCounterIncrement();
! 
! 		/*
! 		 * If necessary, create a TOAST table for the INTO relation. Note that
! 		 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
! 		 * the TOAST table will be visible for insertion.
! 		 */
! 		reloptions = transformRelOptions((Datum) 0,
! 										 into->options,
! 										 "toast",
! 										 validnsps,
! 										 true,
! 										 false);
! 
! 		(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
! 
! 		AlterTableCreateToastTable(intoRelationId, reloptions);
! 
! 		/*
! 		 * And open the constructed table for writing.
! 		 */
! 		intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
! 	}
  
  
  	/*
  	 * Now replace the query's DestReceiver with one for SELECT INTO
***************
*** 2233,2238 ****
--- 2264,2274 ----
  CloseIntoRel(QueryDesc *queryDesc)
  {
  	DR_intorel *myState = (DR_intorel *) queryDesc->dest;
+ 	IntoClause *into = queryDesc->plannedstmt->intoClause;
+ 	Relation	relation;
+ 	Oid			tableOid;
+ 	Oid			tableSpace;
+ 	Oid			OIDTempHeap;
  
  	/* OpenIntoRel might never have gotten called */
  	if (myState && myState->pub.mydest == DestIntoRel && myState->rel)
***************
*** 2246,2251 ****
--- 2282,2301 ----
  		/* close rel, but keep lock until commit */
  		heap_close(myState->rel, NoLock);
  
+ 		/* check if it's REFRESH command */
+ 		if (queryDesc->plannedstmt->intoClause->isrefresh)
+ 		{
+ 			relation		= heap_openrv(into->rel, AccessExclusiveLock);
+ 			tableOid 		= relation->rd_id;
+ 			tableSpace 		= myState->rel->rd_rel->reltablespace;
+ 			OIDTempHeap 	= RelationGetRelid(myState->rel);
+ 
+ 			heap_close(relation, NoLock);
+ 
+ 			/* swap old heap with new one */
+ 			finish_heap_swap(tableOid, OIDTempHeap, false, true, myState->rel->rd_rel->relfrozenxid);
+ 		}
+ 
  		myState->rel = NULL;
  	}
  }
*** ./src/backend/nodes/copyfuncs.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/nodes/copyfuncs.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 930,935 ****
--- 930,936 ----
  	COPY_NODE_FIELD(options);
  	COPY_SCALAR_FIELD(onCommit);
  	COPY_STRING_FIELD(tableSpaceName);
+ 	COPY_SCALAR_FIELD(isrefresh);
  
  	return newnode;
  }
***************
*** 2845,2850 ****
--- 2846,2852 ----
  	COPY_NODE_FIELD(aliases);
  	COPY_NODE_FIELD(query);
  	COPY_SCALAR_FIELD(replace);
+ 	COPY_SCALAR_FIELD(ismaterialized);
  
  	return newnode;
  }
*** ./src/backend/nodes/equalfuncs.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/nodes/equalfuncs.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 119,124 ****
--- 119,125 ----
  	COMPARE_NODE_FIELD(options);
  	COMPARE_SCALAR_FIELD(onCommit);
  	COMPARE_STRING_FIELD(tableSpaceName);
+ 	COMPARE_SCALAR_FIELD(isrefresh);
  
  	return true;
  }
***************
*** 1381,1386 ****
--- 1382,1388 ----
  	COMPARE_NODE_FIELD(aliases);
  	COMPARE_NODE_FIELD(query);
  	COMPARE_SCALAR_FIELD(replace);
+ 	COMPARE_SCALAR_FIELD(ismaterialized);
  
  	return true;
  }
*** ./src/backend/nodes/outfuncs.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/nodes/outfuncs.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 816,821 ****
--- 816,822 ----
  	WRITE_NODE_FIELD(options);
  	WRITE_ENUM_FIELD(onCommit, OnCommitAction);
  	WRITE_STRING_FIELD(tableSpaceName);
+ 	WRITE_BOOL_FIELD(isrefresh);
  }
  
  static void
*** ./src/backend/parser/analyze.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/parser/analyze.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 28,33 ****
--- 28,34 ----
  #include "catalog/pg_type.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
+ #include "nodes/pg_list.h"
  #include "optimizer/var.h"
  #include "parser/analyze.h"
  #include "parser/parse_agg.h"
***************
*** 64,69 ****
--- 65,72 ----
  static void transformLockingClause(ParseState *pstate, Query *qry,
  					   LockingClause *lc, bool pushedDown);
  
+ static SelectStmt *build_refresh_stmt(RangeVar *rv);
+ static bool is_matview_refresh(Node *parseTree);
  
  /*
   * parse_analyze
***************
*** 149,154 ****
--- 152,250 ----
  	return query;
  }
  
+ 
+ /*
+  * Returns true if ALTER TABLE command is REFRESH 
+  * and is the only one command in statement, otherwise return false.
+  * 
+  */
+ static bool
+ is_matview_refresh(Node *parseTree)
+ {
+ 	AlterTableStmt 	*alter;
+ 	AlterTableCmd   *command;
+ 	ListCell 		*l;
+ 	bool			 is_refresh = false;
+ 	
+ 	if (nodeTag(parseTree) == T_AlterTableStmt)
+ 	{
+ 		alter = (AlterTableStmt *) parseTree;
+ 		
+ 		/* check out all types of commands */
+ 		foreach (l, alter->cmds)
+ 		{
+ 			command = (AlterTableCmd *) lfirst(l);
+ 			if (command->subtype == AT_Refresh)
+ 				is_refresh = true;
+ 		}
+ 
+ 		/* if is present REFRESH cmd */
+ 		if (is_refresh)
+ 		{
+ 			/* we are expecting just one command, else show error message */
+ 			if (alter->cmds->length == 1)
+ 				return true;
+ 			else
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("REFRESH cannot be used with other ALTER commands")));
+ 		}
+ 	}
+ 	return false;
+ }
+ 
+ /*
+  * Returns complete parsed SELECT INTO stmt, isRefresh flag
+  * in IntoClause indicate it is REFRESH command.
+  */
+ static SelectStmt *
+ build_refresh_stmt(RangeVar *rv)
+ {
+ 	/* SELECT * INTO matview FROM [matview definition] */
+ 	IntoClause 		*intoClause		= makeNode(IntoClause);
+ 	SelectStmt 		*selectStmt 	= makeNode(SelectStmt);
+ 	ColumnRef		*cref;
+ 	A_Star 			*star;
+ 	ResTarget 		*target;
+ 	
+ 	memset(selectStmt, 0, sizeof(SelectStmt));
+ 	memset(intoClause, 0, sizeof(IntoClause));
+ 
+ 	intoClause->type 			= T_IntoClause;
+ 	intoClause->rel 			= copyObject(rv);
+ 	intoClause->colNames 		= NIL;
+ 	intoClause->options 		= NIL;
+ 	intoClause->onCommit 		= ONCOMMIT_NOOP;
+ 	intoClause->tableSpaceName 	= '\0';
+ 	intoClause->isrefresh		= true;
+ 	
+ 	/* make A_Star node */
+ 	star = (A_Star *) makeNode(A_Star);
+ 	star->type = T_A_Star;
+ 
+ 	/* make ColumnRef node and to the 'fields' add A_Star */
+ 	cref = (ColumnRef *) makeNode(ColumnRef);
+ 	cref->type 			= T_ColumnRef;
+ 	cref->fields 		= list_make1(star);
+ 	cref->location 		= 7;
+ 	/* make ResTarget node and to the 'val' field add list of ColumnRefs */
+ 	target = (ResTarget *) makeNode(ResTarget);
+ 	target->type 		= T_ResTarget;
+ 	target->name 		= "";
+ 	target->indirection = NIL;
+ 	target->location 	= 7;														
+ 	target->val 		= (Node *) cref;
+ 
+ 	/* finally create SelectStmt, append target list and intoClause */
+ 	selectStmt->type 			= T_SelectStmt;
+ 	selectStmt->fromClause 		= list_make1(intoClause->rel);
+ 	selectStmt->targetList		= list_make1(target);
+ 	selectStmt->intoClause		= intoClause;
+ 	selectStmt->op 				= SETOP_NONE;
+ 
+ 	return selectStmt;	
+ }
+ 
  /*
   * transformStmt -
   *	  transform a Parse tree into a Query tree.
***************
*** 191,196 ****
--- 287,324 ----
  			/*
  			 * Special cases
  			 */
+ 		case T_AlterTableStmt:
+ 			{
+ 				AlterTableStmt  *alter = (AlterTableStmt *) parseTree;
+ 				AlterTableCmd   *command;
+ 				SelectStmt      *selectStmt;
+ 
+ 				/*
+ 				 * Check if it's REFRESH-ing materialized view,
+ 				 * if not, do the same as default.
+ 				 */
+ 				if (alter->relkind == OBJECT_MATVIEW && 
+ 					is_matview_refresh(parseTree))
+ 				{
+ 					command = (AlterTableCmd *) linitial(alter->cmds);
+ 					/* build parsed REFRESH (SELECT INTO) stmt manualy */
+ 					selectStmt = build_refresh_stmt(alter->relation);
+ 					/* transfrom as common SELECT INTO */
+ 					result = transformSelectStmt(pstate, selectStmt);
+ 				}
+ 				else
+ 				{
+ 					/*
+ 					 * other statements don't require any transformation; just return
+ 					 * the original parsetree with a Query node plastered on top.
+ 					 */
+ 					result = makeNode(Query);
+ 					result->commandType = CMD_UTILITY;
+ 					result->utilityStmt = (Node *) parseTree;
+ 				}
+ 			}
+ 			break;
+ 
  		case T_DeclareCursorStmt:
  			result = transformDeclareCursorStmt(pstate,
  											(DeclareCursorStmt *) parseTree);
***************
*** 261,266 ****
--- 389,409 ----
  			result = true;
  			break;
  
+ 		case T_AlterTableStmt:
+ 			{
+ 				AlterTableStmt  *alter = (AlterTableStmt *) parseTree;
+ 
+ 				/*
+ 				 * Check if it's REFRESH-ing materialized view,
+ 				 * if not, do the same as default.
+ 				 */
+ 				if (alter->relkind == OBJECT_MATVIEW && is_matview_refresh(parseTree))
+ 					result = true;
+ 				else
+ 					result = false;
+ 			}
+ 			break;
+ 
  		default:
  			/* other utility statements don't have any real parse analysis */
  			result = false;
*** ./src/backend/parser/gram.y.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/parser/gram.y	2010-07-08 15:22:15.000000000 +0200
***************
*** 310,316 ****
  %type <fun_param_mode> arg_class
  %type <typnam>	func_return func_type
  
! %type <boolean>  TriggerForType OptTemp
  %type <oncommit> OnCommitOption
  
  %type <node>	for_locking_item
--- 310,316 ----
  %type <fun_param_mode> arg_class
  %type <typnam>	func_return func_type
  
! %type <boolean>  TriggerForType OptTemp OptMaterialized
  %type <oncommit> OnCommitOption
  
  %type <node>	for_locking_item
***************
*** 502,508 ****
  	LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
  	LOCATION LOCK_P LOGIN_P
  
! 	MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
  	NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
--- 502,508 ----
  	LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
  	LOCATION LOCK_P LOGIN_P
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
  	NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
***************
*** 517,523 ****
  
  	QUOTE
  
! 	RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
  	RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
  	RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
  
--- 517,523 ----
  
  	QUOTE
  
! 	RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REFRESH REINDEX
  	RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
  	RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
  
***************
*** 1598,1603 ****
--- 1598,1611 ----
  					n->relkind = OBJECT_VIEW;
  					$$ = (Node *)n;
  				}
+ 		|	ALTER MATERIALIZED VIEW qualified_name alter_table_cmds
+ 				{
+ 					AlterTableStmt *n = makeNode(AlterTableStmt);
+ 					n->relation = $4;
+ 					n->cmds = $5;
+ 					n->relkind = OBJECT_MATVIEW;
+ 					$$ = (Node *)n;
+ 				}
  		;
  
  alter_table_cmds:
***************
*** 1914,1919 ****
--- 1922,1934 ----
  					n->def = (Node *)$2;
  					$$ = (Node *)n;
  				}
+ 			/* ALTER TABLE <name> REFRESH */
+ 			| REFRESH
+ 				{
+ 					AlterTableCmd *n = makeNode(AlterTableCmd);
+ 					n->subtype = AT_Refresh;
+ 					$$ = (Node *)n;
+ 				}
  		;
  
  alter_column_default:
***************
*** 4046,4051 ****
--- 4061,4067 ----
  drop_type:	TABLE									{ $$ = OBJECT_TABLE; }
  			| SEQUENCE								{ $$ = OBJECT_SEQUENCE; }
  			| VIEW									{ $$ = OBJECT_VIEW; }
+ 			| MATERIALIZED VIEW						{ $$ = OBJECT_MATVIEW; }
  			| INDEX									{ $$ = OBJECT_INDEX; }
  			| TYPE_P								{ $$ = OBJECT_TYPE; }
  			| DOMAIN_P								{ $$ = OBJECT_DOMAIN; }
***************
*** 4102,4108 ****
   *	The COMMENT ON statement can take different forms based upon the type of
   *	the object associated with the comment. The form of the statement is:
   *
!  *	COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
   *				   CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
   *				   CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
   *				   TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
--- 4118,4124 ----
   *	The COMMENT ON statement can take different forms based upon the type of
   *	the object associated with the comment. The form of the statement is:
   *
!  *	COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | [MATERIALIZED] VIEW |
   *				   CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
   *				   CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
   *				   TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
***************
*** 4281,4286 ****
--- 4297,4303 ----
  			| DOMAIN_P							{ $$ = OBJECT_TYPE; }
  			| TYPE_P							{ $$ = OBJECT_TYPE; }
  			| VIEW								{ $$ = OBJECT_VIEW; }
+ 			| MATERIALIZED VIEW					{ $$ = OBJECT_MATVIEW; }
  			| CONVERSION_P						{ $$ = OBJECT_CONVERSION; }
  			| TABLESPACE						{ $$ = OBJECT_TABLESPACE; }
  			| ROLE								{ $$ = OBJECT_ROLE; }
***************
*** 5691,5696 ****
--- 5708,5722 ----
  					n->newname = $6;
  					$$ = (Node *)n;
  				}
+ 			| ALTER MATERIALIZED VIEW qualified_name RENAME TO name
+ 				{
+ 					RenameStmt *n = makeNode(RenameStmt);
+ 					n->renameType = OBJECT_MATVIEW;
+ 					n->relation = $4;
+ 					n->subname = NULL;
+ 					n->newname = $7;
+ 					$$ = (Node *)n;
+ 				}
  			| ALTER INDEX qualified_name RENAME TO name
  				{
  					RenameStmt *n = makeNode(RenameStmt);
***************
*** 5867,5872 ****
--- 5893,5906 ----
  					n->newschema = $6;
  					$$ = (Node *)n;
  				}
+ 			| ALTER MATERIALIZED VIEW qualified_name SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_MATVIEW;
+ 					n->relation = $4;
+ 					n->newschema = $7;
+ 					$$ = (Node *)n;
+ 				}
  			| ALTER TYPE_P any_name SET SCHEMA name
  				{
  					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
***************
*** 6319,6353 ****
  /*****************************************************************************
   *
   *	QUERY:
!  *		CREATE [ OR REPLACE ] [ TEMP ] VIEW <viewname> '('target-list ')'
   *			AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
   *
   *****************************************************************************/
  
! ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $4;
  					n->view->istemp = $2;
! 					n->aliases = $5;
! 					n->query = $7;
  					n->replace = false;
  					$$ = (Node *) n;
  				}
! 		| CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $6;
  					n->view->istemp = $4;
! 					n->aliases = $7;
! 					n->query = $9;
  					n->replace = true;
  					$$ = (Node *) n;
  				}
  		;
  
  opt_check_option:
  		WITH CHECK OPTION
  				{
--- 6353,6394 ----
  /*****************************************************************************
   *
   *	QUERY:
!  *		CREATE [ OR REPLACE ] [ TEMP ] [ MATERIALIZED ] VIEW <viewname> '('target-list ')'
   *			AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
   *
   *****************************************************************************/
  
! ViewStmt: CREATE OptTemp OptMaterialized VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $5;
  					n->view->istemp = $2;
! 					n->ismaterialized = $3;
! 					n->aliases = $6;
! 					n->query = $8;
  					n->replace = false;
  					$$ = (Node *) n;
  				}
! 		| CREATE OR REPLACE OptTemp OptMaterialized VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $7;
  					n->view->istemp = $4;
! 					n->ismaterialized = $5;
! 					n->aliases = $8;
! 					n->query = $10;
  					n->replace = true;
  					$$ = (Node *) n;
  				}
  		;
  
+ 
+ OptMaterialized:	MATERIALIZED	{ 	$$ = true; 	}
+ 		| /* EMPTY */	{ 	$$ = false;	}
+ 		;
+ 
  opt_check_option:
  		WITH CHECK OPTION
  				{
***************
*** 7577,7582 ****
--- 7618,7624 ----
  					$$->options = NIL;
  					$$->onCommit = ONCOMMIT_NOOP;
  					$$->tableSpaceName = NULL;
+ 					$$->isrefresh = FALSE;
  				}
  			| /*EMPTY*/
  				{ $$ = NULL; }
***************
*** 10982,10987 ****
--- 11024,11030 ----
  			| REASSIGN
  			| RECHECK
  			| RECURSIVE
+ 			| REFRESH
  			| REINDEX
  			| RELATIVE_P
  			| RELEASE
***************
*** 11211,11216 ****
--- 11254,11260 ----
  			| LIMIT
  			| LOCALTIME
  			| LOCALTIMESTAMP
+ 			| MATERIALIZED
  			| NOT
  			| NULL_P
  			| OFF
*** ./src/backend/rewrite/rewriteDefine.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/rewrite/rewriteDefine.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 159,165 ****
  	referenced.objectSubId = 0;
  
  	recordDependencyOn(&myself, &referenced,
! 			 (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
  
  	/*
  	 * Also install dependencies on objects referenced in action and qual.
--- 159,165 ----
  	referenced.objectSubId = 0;
  
  	recordDependencyOn(&myself, &referenced,
! 			 (evtype == CMD_SELECT || evtype == CMD_REFRESH) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
  
  	/*
  	 * Also install dependencies on objects referenced in action and qual.
***************
*** 246,252 ****
  	 * Verify relation is of a type that rules can sensibly be applied to.
  	 */
  	if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
! 		event_relation->rd_rel->relkind != RELKIND_VIEW)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  				 errmsg("\"%s\" is not a table or view",
--- 246,253 ----
  	 * Verify relation is of a type that rules can sensibly be applied to.
  	 */
  	if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
! 		event_relation->rd_rel->relkind != RELKIND_VIEW &&
! 		event_relation->rd_rel->relkind != RELKIND_MATVIEW)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  				 errmsg("\"%s\" is not a table or view",
***************
*** 288,294 ****
  					 errhint("Use triggers instead.")));
  	}
  
! 	if (event_type == CMD_SELECT)
  	{
  		/*
  		 * Rules ON SELECT are restricted to view definitions
--- 289,295 ----
  					 errhint("Use triggers instead.")));
  	}
  
! 	if (event_type == CMD_SELECT || event_type == CMD_REFRESH)
  	{
  		/*
  		 * Rules ON SELECT are restricted to view definitions
***************
*** 313,319 ****
  		 * ... the one action must be a SELECT, ...
  		 */
  		query = (Query *) linitial(action);
! 		if (!is_instead ||
  			query->commandType != CMD_SELECT ||
  			query->utilityStmt != NULL ||
  			query->intoClause != NULL)
--- 314,320 ----
  		 * ... the one action must be a SELECT, ...
  		 */
  		query = (Query *) linitial(action);
! 		if ((!is_instead && (event_relation->rd_rel->relkind != RELKIND_MATVIEW)) ||
  			query->commandType != CMD_SELECT ||
  			query->utilityStmt != NULL ||
  			query->intoClause != NULL)
***************
*** 349,355 ****
  				RewriteRule *rule;
  
  				rule = event_relation->rd_rules->rules[i];
! 				if (rule->event == CMD_SELECT)
  					ereport(ERROR,
  						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
  						   errmsg("\"%s\" is already a view",
--- 350,356 ----
  				RewriteRule *rule;
  
  				rule = event_relation->rd_rules->rules[i];
! 				if (rule->event == CMD_SELECT || rule->event == CMD_REFRESH)
  					ereport(ERROR,
  						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
  						   errmsg("\"%s\" is already a view",
***************
*** 499,505 ****
  	 *
  	 * XXX what about getting rid of its TOAST table?  For now, we don't.
  	 */
! 	if (RelisBecomingView)
  		RelationDropStorage(event_relation);
  
  	/* Close rel, but keep lock till commit... */
--- 500,506 ----
  	 *
  	 * XXX what about getting rid of its TOAST table?  For now, we don't.
  	 */
! 	if (RelisBecomingView && (event_relation->rd_rel->relkind != RELKIND_MATVIEW))
  		RelationDropStorage(event_relation);
  
  	/* Close rel, but keep lock till commit... */
*** ./src/backend/rewrite/rewriteHandler.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/rewrite/rewriteHandler.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 1390,1396 ****
  		for (i = 0; i < rules->numLocks; i++)
  		{
  			rule = rules->rules[i];
! 			if (rule->event != CMD_SELECT)
  				continue;
  
  			if (rule->attrno > 0)
--- 1390,1396 ----
  		for (i = 0; i < rules->numLocks; i++)
  		{
  			rule = rules->rules[i];
! 			if (rule->event != CMD_SELECT && rule->event != CMD_REFRESH)
  				continue;
  
  			if (rule->attrno > 0)
***************
*** 1422,1427 ****
--- 1422,1441 ----
  			{
  				rule = lfirst(l);
  
+ 				/*
+ 				 * There are two cases when we want to prevent firing rule:
+ 				 * 1) command is just SELECT stmt
+ 				 * 2) commant is SELECT INTO stmt, not REFRESHing MV
+ 				 */
+ 				if ((rule->event == CMD_REFRESH && parsetree->intoClause == NULL)
+ 					||
+ 					(rule->event == CMD_REFRESH && 
+ 					 parsetree->intoClause != NULL &&
+ 					 !parsetree->intoClause->isrefresh))
+ 				{
+ 					continue;
+ 				}
+ 					
  				parsetree = ApplyRetrieveRule(parsetree,
  											  rule,
  											  rt_index,
***************
*** 1659,1664 ****
--- 1673,1708 ----
  		rt_entry_relation = heap_open(rt_entry->relid, NoLock);
  
  		/*
+ 		 * Inserting, updating or deleting row in materilized views are not allowed
+ 		 */
+ 		if (rt_entry_relation->rd_rel->relkind == RELKIND_MATVIEW)
+ 		{
+ 			heap_close(rt_entry_relation, NoLock);
+ 			switch (parsetree->commandType)
+ 			{
+ 				case CMD_INSERT:
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 							 errmsg("cannot insert into a materialized view")));
+ 					break;
+ 				case CMD_UPDATE:
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 							 errmsg("cannot update a materialized view")));
+ 					break;
+ 				case CMD_DELETE:
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 							 errmsg("cannot delete from a materialized view")));
+ 					break;
+ 				default:
+ 					elog(ERROR, "unrecognized commandType: %d",
+ 						 (int) parsetree->commandType);
+ 					break;
+ 			}
+ 		}
+ 
+ 		/*
  		 * If it's an INSERT or UPDATE, rewrite the targetlist into standard
  		 * form.  This will be needed by the planner anyway, and doing it now
  		 * ensures that any references to NEW.field will behave sanely.
*** ./src/backend/rewrite/rewriteSupport.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/rewrite/rewriteSupport.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 69,75 ****
  	{
  		/* Do the update */
  		classForm->relhasrules = relHasRules;
! 		if (relIsBecomingView)
  			classForm->relkind = RELKIND_VIEW;
  
  		simple_heap_update(relationRelation, &tuple->t_self, tuple);
--- 69,77 ----
  	{
  		/* Do the update */
  		classForm->relhasrules = relHasRules;
! 
! 		/* do not change RELKIND if its Materialized View */
! 		if (relIsBecomingView && classForm->relkind != RELKIND_MATVIEW)
  			classForm->relkind = RELKIND_VIEW;
  
  		simple_heap_update(relationRelation, &tuple->t_self, tuple);
*** ./src/backend/tcop/utility.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/tcop/utility.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 606,611 ****
--- 606,612 ----
  					case OBJECT_TABLE:
  					case OBJECT_SEQUENCE:
  					case OBJECT_VIEW:
+ 					case OBJECT_MATVIEW:
  					case OBJECT_INDEX:
  						RemoveRelations(stmt);
  						break;
***************
*** 1544,1549 ****
--- 1545,1553 ----
  				case OBJECT_VIEW:
  					tag = "DROP VIEW";
  					break;
+ 				case OBJECT_MATVIEW:
+ 					tag = "DROP MATERIALIZED VIEW";
+ 					break;
  				case OBJECT_INDEX:
  					tag = "DROP INDEX";
  					break;
***************
*** 1637,1642 ****
--- 1641,1649 ----
  				case OBJECT_VIEW:
  					tag = "ALTER VIEW";
  					break;
+ 				case OBJECT_MATVIEW:
+ 					tag = "ALTER MATERIALIZED VIEW";
+ 					break;
  				case OBJECT_TSPARSER:
  					tag = "ALTER TEXT SEARCH PARSER";
  					break;
***************
*** 1694,1699 ****
--- 1701,1709 ----
  				case OBJECT_VIEW:
  					tag = "ALTER VIEW";
  					break;
+ 				case OBJECT_MATVIEW:
+ 					tag = "ALTER MATERIALIZED VIEW";
+ 					break;
  				default:
  					tag = "???";
  					break;
***************
*** 1775,1780 ****
--- 1785,1793 ----
  				case OBJECT_VIEW:
  					tag = "ALTER VIEW";
  					break;
+ 				case OBJECT_MATVIEW:
+ 					tag = "ALTER MATERIALIZED VIEW";
+ 					break;
  				default:
  					tag = "???";
  					break;
***************
*** 1847,1853 ****
  			break;
  
  		case T_ViewStmt:
! 			tag = "CREATE VIEW";
  			break;
  
  		case T_CreateFunctionStmt:
--- 1860,1870 ----
  			break;
  
  		case T_ViewStmt:
! 			{
! 				ViewStmt *stmt = (ViewStmt *) parsetree;
! 
! 				tag = (stmt->ismaterialized) ? "CREATE MATERIALIZED VIEW" : "CREATE VIEW";
! 			}
  			break;
  
  		case T_CreateFunctionStmt:
*** ./src/backend/utils/adt/dbsize.c.orig	2010-07-06 10:43:16.000000000 +0200
--- ./src/backend/utils/adt/dbsize.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 541,546 ****
--- 541,547 ----
  		case RELKIND_INDEX:
  		case RELKIND_SEQUENCE:
  		case RELKIND_TOASTVALUE:
+ 		case RELKIND_MATVIEW:
  			/* okay, these have storage */
  			if (relform->relfilenode)
  				result = relform->relfilenode;
***************
*** 588,593 ****
--- 589,595 ----
  		case RELKIND_INDEX:
  		case RELKIND_SEQUENCE:
  		case RELKIND_TOASTVALUE:
+ 		case RELKIND_MATVIEW:
  			/* okay, these have storage */
  
  			/* This logic should match RelationInitPhysicalAddr */
*** ./src/backend/utils/adt/ruleutils.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/backend/utils/adt/ruleutils.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 2384,2391 ****
  
  	query = (Query *) linitial(actions);
  
! 	if (ev_type != '1' || ev_attr >= 0 || !is_instead ||
! 		strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
  	{
  		appendStringInfo(buf, "Not a view");
  		return;
--- 2384,2393 ----
  
  	query = (Query *) linitial(actions);
  
! 	/* ev_type == 1 SELECT, ev_type == 6 REFRESH */
! 	if (ev_attr >= 0 || strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT ||
! 		((!is_instead || ev_type != '1') &&
! 		(is_instead || ev_type != '6')))
  	{
  		appendStringInfo(buf, "Not a view");
  		return;
*** ./src/bin/pg_dump/pg_backup_archiver.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/bin/pg_dump/pg_backup_archiver.c	2010-07-08 21:10:12.000000000 +0200
***************
*** 2699,2705 ****
  	const char *type = te->desc;
  
  	/* Use ALTER TABLE for views and sequences */
! 	if (strcmp(type, "VIEW") == 0 || strcmp(type, "SEQUENCE") == 0)
  		type = "TABLE";
  
  	/* objects named by a schema and name */
--- 2699,2705 ----
  	const char *type = te->desc;
  
  	/* Use ALTER TABLE for views and sequences */
! 	if (strcmp(type, "VIEW") == 0 || strcmp(type, "MATERIALIZED VIEW") == 0 || strcmp(type, "SEQUENCE") == 0)
  		type = "TABLE";
  
  	/* objects named by a schema and name */
***************
*** 2898,2903 ****
--- 2898,2904 ----
  			strcmp(te->desc, "TABLE") == 0 ||
  			strcmp(te->desc, "TYPE") == 0 ||
  			strcmp(te->desc, "VIEW") == 0 ||
+ 			strcmp(te->desc, "MATERIALIZED VIEW") == 0 ||
  			strcmp(te->desc, "SEQUENCE") == 0 ||
  			strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
  			strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
***************
*** 3010,3015 ****
--- 3011,3017 ----
  
  		AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
  
+ 
  		if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
  			die_horribly(AH, modulename, "unsupported version (%d.%d) in file header\n",
  						 AH->vmaj, AH->vmin);
*** ./src/bin/pg_dump/pg_dump.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/bin/pg_dump/pg_dump.c	2010-07-08 21:12:23.000000000 +0200
***************
*** 1434,1439 ****
--- 1434,1442 ----
  		/* Skip VIEWs (no data to dump) */
  		if (tblinfo[i].relkind == RELKIND_VIEW)
  			continue;
+ 		/* Skip MATERIALIZED VIEWs (redundant data are not dumped) */
+ 		if (tblinfo[i].relkind == RELKIND_MATVIEW)
+ 			continue;
  		/* Skip SEQUENCEs (handled elsewhere) */
  		if (tblinfo[i].relkind == RELKIND_SEQUENCE)
  			continue;
***************
*** 3464,3475 ****
  						  "d.objsubid = 0 AND "
  						  "d.refclassid = c.tableoid AND d.deptype = 'a') "
  					   "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
! 						  "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
  						  "ORDER BY c.oid",
  						  username_subquery,
  						  RELKIND_SEQUENCE,
  						  RELKIND_RELATION, RELKIND_SEQUENCE,
! 						  RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
  	}
  	else if (g_fout->remoteVersion >= 80400)
  	{
--- 3467,3478 ----
  						  "d.objsubid = 0 AND "
  						  "d.refclassid = c.tableoid AND d.deptype = 'a') "
  					   "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
! 						  "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
  						  "ORDER BY c.oid",
  						  username_subquery,
  						  RELKIND_SEQUENCE,
  						  RELKIND_RELATION, RELKIND_SEQUENCE,
! 						  RELKIND_VIEW, RELKIND_MATVIEW, RELKIND_COMPOSITE_TYPE);
  	}
  	else if (g_fout->remoteVersion >= 80400)
  	{
***************
*** 4515,4522 ****
  			 * for the rule affect the table's positioning. Other rules are
  			 * forced to appear after their table.
  			 */
! 			if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW &&
! 				ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
  			{
  				addObjectDependency(&ruleinfo[i].ruletable->dobj,
  									ruleinfo[i].dobj.dumpId);
--- 4518,4528 ----
  			 * for the rule affect the table's positioning. Other rules are
  			 * forced to appear after their table.
  			 */
! 			if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW && 
! 				ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead) ||
! 				(ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW &&
! 				ruleinfo[i].ev_type == '6' && !ruleinfo[i].is_instead)
! 			   )
  			{
  				addObjectDependency(&ruleinfo[i].ruletable->dobj,
  									ruleinfo[i].dobj.dumpId);
***************
*** 10574,10579 ****
--- 10580,10639 ----
  
  		PQclear(res);
  	}
+ 	else if (tbinfo->relkind == RELKIND_MATVIEW)
+ 	{
+ 		char	   *viewdef;
+ 
+ 		reltypename = "MATERIALIZED VIEW";
+ 
+ 		/* Fetch the view definition */
+ 		appendPQExpBuffer(query,
+ 						  "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
+ 						  tbinfo->dobj.catId.oid);
+ 
+ 		res = PQexec(g_conn, query->data);
+ 		check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ 
+ 		if (PQntuples(res) != 1)
+ 		{
+ 			if (PQntuples(res) < 1)
+ 				write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
+ 						  tbinfo->dobj.name);
+ 			else
+ 				write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
+ 						  tbinfo->dobj.name);
+ 			exit_nicely();
+ 		}
+ 
+ 		viewdef = PQgetvalue(res, 0, 0);
+ 
+ 		if (strlen(viewdef) == 0)
+ 		{
+ 			write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
+ 					  tbinfo->dobj.name);
+ 			exit_nicely();
+ 		}
+ 
+ 		/*
+ 		 * DROP must be fully qualified in case same name appears in
+ 		 * pg_catalog
+ 		 */
+ 		appendPQExpBuffer(delq, "DROP MATERIALIZED VIEW %s.",
+ 						  fmtId(tbinfo->dobj.namespace->dobj.name));
+ 		appendPQExpBuffer(delq, "%s;\n",
+ 						  fmtId(tbinfo->dobj.name));
+ 
+ 		if (binary_upgrade)
+ 			binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
+ 
+ 		appendPQExpBuffer(q, "CREATE MATERIALIZED VIEW %s AS\n    %s\n\n",
+ 						  fmtId(tbinfo->dobj.name), viewdef);
+ 
+ 		appendPQExpBuffer(q, "ALTER MATERIALIZED VIEW %s REFRESH;\n",
+ 						  fmtId(tbinfo->dobj.name));
+ 
+ 		PQclear(res);
+ 	}
  	else
  	{
  		reltypename = "TABLE";
*** ./src/bin/pg_dump/pg_dump_sort.c.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/bin/pg_dump/pg_dump_sort.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 823,829 ****
  		return;
  	}
  
! 	/* Indirect loop involving view and ON SELECT rule */
  	if (nLoop > 2)
  	{
  		for (i = 0; i < nLoop; i++)
--- 823,854 ----
  		return;
  	}
  
! 	/* Materialized View and its ON REFRESH rule */
! 	if (nLoop == 2 &&
! 		loop[0]->objType == DO_TABLE &&
! 		loop[1]->objType == DO_RULE &&
! 		((RuleInfo *) loop[1])->ev_type == '6' &&
! 		!((RuleInfo *) loop[1])->is_instead &&
! 		((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
! 	{
! 		repairViewRuleLoop(loop[0], loop[1]);
! 		return;
! 	}
! 	if (nLoop == 2 &&
! 		loop[1]->objType == DO_TABLE &&
! 		loop[0]->objType == DO_RULE &&
! 		((RuleInfo *) loop[0])->ev_type == '6' &&
! 		!((RuleInfo *) loop[0])->is_instead &&
! 		((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
! 	{
! 		repairViewRuleLoop(loop[1], loop[0]);
! 		return;
! 	}
! 
! 	/* 
! 	 * Indirect loop involving view and ON SELECT rule 
! 	 * and materialized view and its ON REFRESH rule 
! 	 */
  	if (nLoop > 2)
  	{
  		for (i = 0; i < nLoop; i++)
***************
*** 832,837 ****
--- 857,863 ----
  			{
  				for (j = 0; j < nLoop; j++)
  				{
+ 					/* view and ON SELECT rule */
  					if (loop[j]->objType == DO_RULE &&
  						((RuleInfo *) loop[j])->ev_type == '1' &&
  						((RuleInfo *) loop[j])->is_instead &&
***************
*** 840,845 ****
--- 866,880 ----
  						repairViewRuleMultiLoop(loop[i], loop[j]);
  						return;
  					}
+ 					/* materialized view and ON REFRESH rule */
+ 					if (loop[j]->objType == DO_RULE &&
+ 						((RuleInfo *) loop[j])->ev_type == '6' &&
+ 						!((RuleInfo *) loop[j])->is_instead &&
+ 						((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
+ 					{
+ 						repairViewRuleMultiLoop(loop[i], loop[j]);
+ 						return;
+ 					}
  				}
  			}
  		}
*** ./src/bin/psql/command.c.orig	2010-07-06 10:43:17.000000000 +0200
--- ./src/bin/psql/command.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 346,352 ****
  					success = describeTableDetails(pattern, show_verbose, show_system);
  				else
  					/* standard listing of interesting things */
! 					success = listTables("tvs", NULL, show_verbose, show_system);
  				break;
  			case 'a':
  				success = describeAggregates(pattern, show_verbose, show_system);
--- 346,352 ----
  					success = describeTableDetails(pattern, show_verbose, show_system);
  				else
  					/* standard listing of interesting things */
! 					success = listTables("tvms", NULL, show_verbose, show_system);
  				break;
  			case 'a':
  				success = describeAggregates(pattern, show_verbose, show_system);
***************
*** 407,412 ****
--- 407,413 ----
  				break;
  			case 't':
  			case 'v':
+ 			case 'm':
  			case 'i':
  			case 's':
  				success = listTables(&cmd[1], pattern, show_verbose, show_system);
*** ./src/bin/psql/describe.c.orig	2010-07-06 10:43:17.000000000 +0200
--- ./src/bin/psql/describe.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 911,923 ****
  					  "  n.nspname as nspname,\n"
  					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
  					  "  CAST(\n"
! 					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
  					  "  AS pg_catalog.text) as object\n"
  					  "  FROM pg_catalog.pg_class c\n"
  	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
! 					  "  WHERE c.relkind IN ('r', 'v', 'i', 'S')\n",
  					  gettext_noop("table"),
  					  gettext_noop("view"),
  					  gettext_noop("index"),
  					  gettext_noop("sequence"));
  
--- 911,924 ----
  					  "  n.nspname as nspname,\n"
  					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
  					  "  CAST(\n"
! 					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'm' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
  					  "  AS pg_catalog.text) as object\n"
  					  "  FROM pg_catalog.pg_class c\n"
  	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
! 					  "  WHERE c.relkind IN ('r', 'v', 'm', 'i', 'S')\n",
  					  gettext_noop("table"),
  					  gettext_noop("view"),
+ 					  gettext_noop("materialized view"),
  					  gettext_noop("index"),
  					  gettext_noop("sequence"));
  
***************
*** 1266,1271 ****
--- 1267,1276 ----
  			printfPQExpBuffer(&title, _("View \"%s.%s\""),
  							  schemaname, relationname);
  			break;
+ 		case 'm':
+ 			printfPQExpBuffer(&title, _("Materialized View \"%s.%s\""),
+ 							  schemaname, relationname);
+ 			break;
  		case 'S':
  			printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
  							  schemaname, relationname);
***************
*** 1299,1305 ****
  	headers[1] = gettext_noop("Type");
  	cols = 2;
  
! 	if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
  	{
  		show_modifiers = true;
  		headers[cols++] = gettext_noop("Modifiers");
--- 1304,1310 ----
  	headers[1] = gettext_noop("Type");
  	cols = 2;
  
! 	if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || tableinfo.relkind == 'm')
  	{
  		show_modifiers = true;
  		headers[cols++] = gettext_noop("Modifiers");
***************
*** 1325,1331 ****
  		printTableAddHeader(&cont, headers[i], true, 'l');
  
  	/* Check if table is a view */
! 	if (tableinfo.relkind == 'v' && verbose)
  	{
  		PGresult   *result;
  
--- 1330,1336 ----
  		printTableAddHeader(&cont, headers[i], true, 'l');
  
  	/* Check if table is a view */
! 	if ((tableinfo.relkind == 'v' || tableinfo.relkind == 'm') && verbose)
  	{
  		PGresult   *result;
  
***************
*** 1527,1533 ****
  			PQclear(result);
  		}
  	}
! 	else if (tableinfo.relkind == 'r')
  	{
  		/* Footer information about a table */
  		PGresult   *result = NULL;
--- 1532,1538 ----
  			PQclear(result);
  		}
  	}
! 	if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm')
  	{
  		/* Footer information about a table */
  		PGresult   *result = NULL;
***************
*** 1724,1730 ****
  		/* print rules */
  		if (tableinfo.hasrules)
  		{
! 			if (pset.sversion >= 80300)
  			{
  				printfPQExpBuffer(&buf,
  								  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
--- 1729,1744 ----
  		/* print rules */
  		if (tableinfo.hasrules)
  		{
! 			if (pset.sversion >= 90000)
! 			{
! 				printfPQExpBuffer(&buf,
! 								  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
! 								  "ev_enabled\n"
! 								  "FROM pg_catalog.pg_rewrite r\n"
! 								  "WHERE r.ev_class = '%s' AND r.rulename <> '_RETURN' ORDER BY 1",
! 								  oid);
! 			}
! 			else if (pset.sversion >= 80300)
  			{
  				printfPQExpBuffer(&buf,
  								  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
***************
*** 2006,2012 ****
  		}
  
  		/* OIDs and options */
! 		if (verbose)
  		{
  			const char *s = _("Has OIDs");
  
--- 2020,2026 ----
  		}
  
  		/* OIDs and options */
! 		if (verbose && tableinfo.relkind != 'm')
  		{
  			const char *s = _("Has OIDs");
  
***************
*** 2079,2085 ****
  					  Oid tablespace, const bool newline)
  {
  	/* relkinds for which we support tablespaces */
! 	if (relkind == 'r' || relkind == 'i')
  	{
  		/*
  		 * We ignore the database default tablespace so that users not using
--- 2093,2099 ----
  					  Oid tablespace, const bool newline)
  {
  	/* relkinds for which we support tablespaces */
! 	if (relkind == 'r' || relkind == 'i' || relkind == 'm')
  	{
  		/*
  		 * We ignore the database default tablespace so that users not using
***************
*** 2339,2344 ****
--- 2353,2359 ----
   * t - tables
   * i - indexes
   * v - views
+  * m - materialized views
   * s - sequences
   * (any order of the above is fine)
   * If tabtypes is empty, we default to \dtvs.
***************
*** 2349,2354 ****
--- 2364,2370 ----
  	bool		showTables = strchr(tabtypes, 't') != NULL;
  	bool		showIndexes = strchr(tabtypes, 'i') != NULL;
  	bool		showViews = strchr(tabtypes, 'v') != NULL;
+ 	bool		showMatViews = strchr(tabtypes, 'm') != NULL;
  	bool		showSeq = strchr(tabtypes, 's') != NULL;
  
  	PQExpBufferData buf;
***************
*** 2356,2363 ****
  	printQueryOpt myopt = pset.popt;
  	static const bool translate_columns[] = {false, false, true, false, false, false, false};
  
! 	if (!(showTables || showIndexes || showViews || showSeq))
! 		showTables = showViews = showSeq = true;
  
  	initPQExpBuffer(&buf);
  
--- 2372,2379 ----
  	printQueryOpt myopt = pset.popt;
  	static const bool translate_columns[] = {false, false, true, false, false, false, false};
  
! 	if (!(showTables || showIndexes || showViews || showMatViews || showSeq))
! 		showTables = showViews = showMatViews = showSeq = true;
  
  	initPQExpBuffer(&buf);
  
***************
*** 2368,2379 ****
  	printfPQExpBuffer(&buf,
  					  "SELECT n.nspname as \"%s\",\n"
  					  "  c.relname as \"%s\",\n"
! 					  "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' END as \"%s\",\n"
  					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("table"),
  					  gettext_noop("view"),
  					  gettext_noop("index"),
  					  gettext_noop("sequence"),
  					  gettext_noop("special"),
--- 2384,2396 ----
  	printfPQExpBuffer(&buf,
  					  "SELECT n.nspname as \"%s\",\n"
  					  "  c.relname as \"%s\",\n"
! 					  "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s'  WHEN 'm' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' END as \"%s\",\n"
  					  "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("table"),
  					  gettext_noop("view"),
+ 					  gettext_noop("materialized view"),
  					  gettext_noop("index"),
  					  gettext_noop("sequence"),
  					  gettext_noop("special"),
***************
*** 2407,2412 ****
--- 2424,2431 ----
  		appendPQExpBuffer(&buf, "'r',");
  	if (showViews)
  		appendPQExpBuffer(&buf, "'v',");
+ 	if (showMatViews)
+ 		appendPQExpBuffer(&buf, "'m',");
  	if (showIndexes)
  		appendPQExpBuffer(&buf, "'i',");
  	if (showSeq)
*** ./src/bin/psql/help.c.orig	2010-07-06 10:43:17.000000000 +0200
--- ./src/bin/psql/help.c	2010-07-08 15:22:15.000000000 +0200
***************
*** 194,200 ****
  
  	fprintf(output, _("Informational\n"));
  	fprintf(output, _("  (options: S = show system objects, + = additional detail)\n"));
! 	fprintf(output, _("  \\d[S+]                 list tables, views, and sequences\n"));
  	fprintf(output, _("  \\d[S+]  NAME           describe table, view, sequence, or index\n"));
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
--- 194,200 ----
  
  	fprintf(output, _("Informational\n"));
  	fprintf(output, _("  (options: S = show system objects, + = additional detail)\n"));
! 	fprintf(output, _("  \\d[S+]                 list tables, views, materialized views, and sequences\n"));
  	fprintf(output, _("  \\d[S+]  NAME           describe table, view, sequence, or index\n"));
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
***************
*** 223,228 ****
--- 223,229 ----
  	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
  	fprintf(output, _("  \\du[+]  [PATTERN]      list roles (users)\n"));
  	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
+ 	fprintf(output, _("  \\dm[S+] [PATTERN]      list materialized views\n"));
  	fprintf(output, _("  \\l[+]                  list all databases\n"));
  	fprintf(output, _("  \\z      [PATTERN]      same as \\dp\n"));
  	fprintf(output, "\n");
*** ./src/include/catalog/pg_class.h.orig	2010-07-08 20:32:53.000000000 +0200
--- ./src/include/catalog/pg_class.h	2010-07-08 15:22:15.000000000 +0200
***************
*** 147,152 ****
--- 147,153 ----
  #define		  RELKIND_UNCATALOGED	  'u'		/* temporary heap */
  #define		  RELKIND_TOASTVALUE	  't'		/* moved off huge values */
  #define		  RELKIND_VIEW			  'v'		/* view */
+ #define		  RELKIND_MATVIEW		  'm'		/* materialized view */
  #define		  RELKIND_COMPOSITE_TYPE  'c'		/* composite type */
  
  #endif   /* PG_CLASS_H */
*** ./src/include/nodes/nodes.h.orig	2010-07-08 20:32:54.000000000 +0200
--- ./src/include/nodes/nodes.h	2010-07-08 15:22:15.000000000 +0200
***************
*** 512,517 ****
--- 512,518 ----
  	CMD_DELETE,
  	CMD_UTILITY,				/* cmds like create, destroy, copy, vacuum,
  								 * etc. */
+ 	CMD_REFRESH,				/* refreshing tables, like materialized views */
  	CMD_NOTHING					/* dummy command for instead nothing rules
  								 * with qual */
  } CmdType;
*** ./src/include/nodes/parsenodes.h.orig	2010-07-08 20:32:54.000000000 +0200
--- ./src/include/nodes/parsenodes.h	2010-07-08 15:22:15.000000000 +0200
***************
*** 1055,1060 ****
--- 1055,1061 ----
  	OBJECT_INDEX,
  	OBJECT_LANGUAGE,
  	OBJECT_LARGEOBJECT,
+ 	OBJECT_MATVIEW,
  	OBJECT_OPCLASS,
  	OBJECT_OPERATOR,
  	OBJECT_OPFAMILY,
***************
*** 1150,1156 ****
  	AT_EnableReplicaRule,		/* ENABLE REPLICA RULE name */
  	AT_DisableRule,				/* DISABLE RULE name */
  	AT_AddInherit,				/* INHERIT parent */
! 	AT_DropInherit				/* NO INHERIT parent */
  } AlterTableType;
  
  typedef struct AlterTableCmd	/* one subcommand of an ALTER TABLE */
--- 1151,1158 ----
  	AT_EnableReplicaRule,		/* ENABLE REPLICA RULE name */
  	AT_DisableRule,				/* DISABLE RULE name */
  	AT_AddInherit,				/* INHERIT parent */
! 	AT_DropInherit,				/* NO INHERIT parent */
! 	AT_Refresh					/* alter materialized view REFRESH */
  } AlterTableType;
  
  typedef struct AlterTableCmd	/* one subcommand of an ALTER TABLE */
***************
*** 2181,2186 ****
--- 2183,2189 ----
  	List	   *aliases;		/* target column names */
  	Node	   *query;			/* the SELECT query */
  	bool		replace;		/* replace an existing view? */
+ 	bool		ismaterialized;		/* materialize view? */
  } ViewStmt;
  
  /* ----------------------
*** ./src/include/nodes/primnodes.h.orig	2010-07-08 20:32:54.000000000 +0200
--- ./src/include/nodes/primnodes.h	2010-07-08 15:22:15.000000000 +0200
***************
*** 91,96 ****
--- 91,97 ----
  	List	   *options;		/* options from WITH clause */
  	OnCommitAction onCommit;	/* what do we do at COMMIT? */
  	char	   *tableSpaceName; /* table space to use, or NULL */
+ 	bool		isrefresh;		/* refreshing table? */
  } IntoClause;
  
  
*** ./src/include/parser/kwlist.h.orig	2010-07-08 20:32:54.000000000 +0200
--- ./src/include/parser/kwlist.h	2010-07-08 15:22:15.000000000 +0200
***************
*** 229,234 ****
--- 229,235 ----
  PG_KEYWORD("login", LOGIN_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
+ PG_KEYWORD("materialized", MATERIALIZED, RESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
***************
*** 302,307 ****
--- 303,309 ----
  PG_KEYWORD("recheck", RECHECK, UNRESERVED_KEYWORD)
  PG_KEYWORD("recursive", RECURSIVE, UNRESERVED_KEYWORD)
  PG_KEYWORD("references", REFERENCES, RESERVED_KEYWORD)
+ PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD)
  PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD)
  PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD)