Bruce Momjian <br...@momjian.us> writes:
> OK, how are we for bata packaging on Monday?  I don't see how we can do
> that until we decide on how to handle unlogged materialized views.

In the interests of moving the discussion along, attached are draft
patches to show what I think we should do in 9.3.  The first patch
disables the unlogged-matviews feature at the user level (and perhaps
could be reverted someday), while the second one moves
scannability-state storage into a pg_class column.

I had previously proposed that we keep scannability state in reloptions,
but that turns out not to be terribly easy because the relcache doesn't
store the reloptions part of the pg_class row; so this patch does it
with a new boolean column instead.

                        regards, tom lane

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index a7e4e210eeb0ef20bb7ea046f5c72a36cc4ad880..0ed764b353394137afd73c9a013e031e197ae79c 100644
*** a/doc/src/sgml/ref/create_materialized_view.sgml
--- b/doc/src/sgml/ref/create_materialized_view.sgml
*************** PostgreSQL documentation
*** 21,27 ****
  
   <refsynopsisdiv>
  <synopsis>
! CREATE [ UNLOGGED ] MATERIALIZED VIEW <replaceable>table_name</replaceable>
      [ (<replaceable>column_name</replaceable> [, ...] ) ]
      [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
      [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
--- 21,27 ----
  
   <refsynopsisdiv>
  <synopsis>
! CREATE MATERIALIZED VIEW <replaceable>table_name</replaceable>
      [ (<replaceable>column_name</replaceable> [, ...] ) ]
      [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
      [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
*************** CREATE [ UNLOGGED ] MATERIALIZED VIEW <r
*** 55,70 ****
  
    <variablelist>
     <varlistentry>
-     <term><literal>UNLOGGED</></term>
-     <listitem>
-      <para>
-       If specified, the materialized view will be unlogged.
-       Refer to <xref linkend="sql-createtable"> for details.
-      </para>
-     </listitem>
-    </varlistentry>
- 
-    <varlistentry>
      <term><replaceable>table_name</replaceable></term>
      <listitem>
       <para>
--- 55,60 ----
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index fb28e471685c7462e8076556a78ef45b561f3d2e..53ff4ed9c1eed923219ee3f0bfc1eff6ccdcf202 100644
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
*************** transformCreateTableAsStmt(ParseState *p
*** 2167,2172 ****
--- 2167,2184 ----
  					 errmsg("materialized views may not be defined using bound parameters")));
  
  		/*
+ 		 * For now, we disallow unlogged materialized views, because it
+ 		 * seems like a bad idea for them to just go to empty after a crash.
+ 		 * (If we could make them go into unscannable state, that would be
+ 		 * better, but that requires catalog changes which crash recovery
+ 		 * can't presently do.)
+ 		 */
+ 		if (stmt->into->rel->relpersistence == RELPERSISTENCE_UNLOGGED)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("materialized views cannot be UNLOGGED")));
+ 
+ 		/*
  		 * At runtime, we'll need a copy of the parsed-but-not-rewritten Query
  		 * for purposes of creating the view's ON SELECT rule.  We stash that
  		 * in the IntoClause because that's where intorel_startup() can
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index e87775679ac9eb059af776e6e4c20e3bed7360da..06bb2551a83480361ddfce5ad02b76240d9eaf63 100644
*** a/src/test/regress/expected/matview.out
--- b/src/test/regress/expected/matview.out
*************** SELECT * FROM tvvm;
*** 279,337 ****
  (1 row)
  
  -- test diemv when the mv does not exist
! DROP MATERIALIZED VIEW IF EXISTS tum;
! NOTICE:  materialized view "tum" does not exist, skipping
! -- make sure that an unlogged materialized view works (in the absence of a crash)
! CREATE UNLOGGED MATERIALIZED VIEW tum AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
! SELECT pg_relation_is_scannable('tum'::regclass);
!  pg_relation_is_scannable 
! --------------------------
!  f
! (1 row)
! 
! SELECT * FROM tum;
! ERROR:  materialized view "tum" has not been populated
! HINT:  Use the REFRESH MATERIALIZED VIEW command.
! REFRESH MATERIALIZED VIEW tum;
! SELECT pg_relation_is_scannable('tum'::regclass);
!  pg_relation_is_scannable 
! --------------------------
!  t
! (1 row)
! 
! SELECT * FROM tum;
!  type | totamt 
! ------+--------
!  y    |     12
!  z    |     24
!  x    |      5
! (3 rows)
! 
! REFRESH MATERIALIZED VIEW tum WITH NO DATA;
! SELECT pg_relation_is_scannable('tum'::regclass);
!  pg_relation_is_scannable 
! --------------------------
!  f
! (1 row)
! 
! SELECT * FROM tum;
! ERROR:  materialized view "tum" has not been populated
! HINT:  Use the REFRESH MATERIALIZED VIEW command.
! REFRESH MATERIALIZED VIEW tum WITH DATA;
! SELECT pg_relation_is_scannable('tum'::regclass);
!  pg_relation_is_scannable 
! --------------------------
!  t
! (1 row)
! 
! SELECT * FROM tum;
!  type | totamt 
! ------+--------
!  y    |     12
!  z    |     24
!  x    |      5
! (3 rows)
! 
  -- test join of mv and view
  SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM tm m LEFT JOIN tv v USING (type) ORDER BY type;
   type | mtot | vtot 
--- 279,286 ----
  (1 row)
  
  -- test diemv when the mv does not exist
! DROP MATERIALIZED VIEW IF EXISTS no_such_mv;
! NOTICE:  materialized view "no_such_mv" does not exist, skipping
  -- test join of mv and view
  SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM tm m LEFT JOIN tv v USING (type) ORDER BY type;
   type | mtot | vtot 
*************** SELECT type, m.totamt AS mtot, v.totamt 
*** 341,348 ****
   z    |   24 |   24
  (3 rows)
  
- -- test diemv when the mv does exist
- DROP MATERIALIZED VIEW IF EXISTS tum;
  -- make sure that dependencies are reported properly when they block the drop
  DROP TABLE t;
  ERROR:  cannot drop table t because other objects depend on it
--- 290,295 ----
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index 9fbaafac6d0ae609e80f777754d6f71189cc4af0..09a7378133c86d66755dafd1629719a712badfdf 100644
*** a/src/test/regress/sql/matview.sql
--- b/src/test/regress/sql/matview.sql
*************** SELECT * FROM tvmm;
*** 87,114 ****
  SELECT * FROM tvvm;
  
  -- test diemv when the mv does not exist
! DROP MATERIALIZED VIEW IF EXISTS tum;
! 
! -- make sure that an unlogged materialized view works (in the absence of a crash)
! CREATE UNLOGGED MATERIALIZED VIEW tum AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
! SELECT pg_relation_is_scannable('tum'::regclass);
! SELECT * FROM tum;
! REFRESH MATERIALIZED VIEW tum;
! SELECT pg_relation_is_scannable('tum'::regclass);
! SELECT * FROM tum;
! REFRESH MATERIALIZED VIEW tum WITH NO DATA;
! SELECT pg_relation_is_scannable('tum'::regclass);
! SELECT * FROM tum;
! REFRESH MATERIALIZED VIEW tum WITH DATA;
! SELECT pg_relation_is_scannable('tum'::regclass);
! SELECT * FROM tum;
  
  -- test join of mv and view
  SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM tm m LEFT JOIN tv v USING (type) ORDER BY type;
  
- -- test diemv when the mv does exist
- DROP MATERIALIZED VIEW IF EXISTS tum;
- 
  -- make sure that dependencies are reported properly when they block the drop
  DROP TABLE t;
  
--- 87,97 ----
  SELECT * FROM tvvm;
  
  -- test diemv when the mv does not exist
! DROP MATERIALIZED VIEW IF EXISTS no_such_mv;
  
  -- test join of mv and view
  SELECT type, m.totamt AS mtot, v.totamt AS vtot FROM tm m LEFT JOIN tv v USING (type) ORDER BY type;
  
  -- make sure that dependencies are reported properly when they block the drop
  DROP TABLE t;
  
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 6c0ef5bd195bac55e1b3b799d49e60ccda83738a..f6df8a8cedddddbdfefca46a5f47eb2868450c13 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1864,1869 ****
--- 1864,1877 ----
       </row>
  
       <row>
+       <entry><structfield>relisscannable</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry></entry>
+       <entry>True if okay to read contents of relation (this is true for all
+       relations other than some materialized views)</entry>
+      </row>
+ 
+      <row>
        <entry><structfield>relfrozenxid</structfield></entry>
        <entry><type>xid</type></entry>
        <entry></entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 3ae2f23390f05a1c4e1797804813cbc6fa1d84d6..af00527fde3c99d4164f124667c241f91eea34f0 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT pg_type_is_visible('myschema.widg
*** 14239,14248 ****
     </indexterm>
  
     <indexterm>
-     <primary>pg_relation_is_scannable</primary>
-    </indexterm>
- 
-    <indexterm>
      <primary>pg_typeof</primary>
     </indexterm>
  
--- 14239,14244 ----
*************** SELECT pg_type_is_visible('myschema.widg
*** 14411,14421 ****
         <entry>get the path in the file system that this tablespace is located in</entry>
        </row>
        <row>
-        <entry><literal><function>pg_relation_is_scannable(<parameter>relation_oid</parameter>)</function></literal></entry>
-        <entry><type>boolean</type></entry>
-        <entry>is the relation scannable; a materialized view which has not been loaded will not be scannable</entry>
-       </row>
-       <row>
         <entry><literal><function>pg_typeof(<parameter>any</parameter>)</function></literal></entry>
         <entry><type>regtype</type></entry>
         <entry>get the data type of any value</entry>
--- 14407,14412 ----
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0b4c659ff0751a48e69c4b7886a7ba38804eb46a..131e4c601694c22ad7981d85870447eaff9d5013 100644
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
*************** InsertPgClassTuple(Relation pg_class_des
*** 780,785 ****
--- 780,786 ----
  	values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
  	values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
  	values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
+ 	values[Anum_pg_class_relisscannable - 1] = BoolGetDatum(rd_rel->relisscannable);
  	values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
  	values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid);
  	if (relacl != (Datum) 0)
*************** heap_create_init_fork(Relation rel)
*** 1346,1371 ****
  }
  
  /*
-  * Check whether a materialized view is in an initial, unloaded state.
-  *
-  * The check here must match what is set up in heap_create_init_fork().
-  * Currently the init fork is an empty file.  A missing heap is also
-  * considered to be unloaded.
-  */
- bool
- heap_is_matview_init_state(Relation rel)
- {
- 	Assert(rel->rd_rel->relkind == RELKIND_MATVIEW);
- 
- 	RelationOpenSmgr(rel);
- 
- 	if (!smgrexists(rel->rd_smgr, MAIN_FORKNUM))
- 		return true;
- 
- 	return (smgrnblocks(rel->rd_smgr, MAIN_FORKNUM) < 1);
- }
- 
- /*
   *		RelationRemoveInheritance
   *
   * Formerly, this routine checked for child relations and aborted the
--- 1347,1352 ----
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 57adbf607de1a866727e905c8387c7a4618b4237..5dbc6632ad59a52af0a89163af1cb42b0cec40fc 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_matviews AS
*** 101,107 ****
          pg_get_userbyid(C.relowner) AS matviewowner,
          T.spcname AS tablespace,
          C.relhasindex AS hasindexes,
!         pg_relation_is_scannable(C.oid) AS isscannable,
          pg_get_viewdef(C.oid) AS definition
      FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
           LEFT JOIN pg_tablespace T ON (T.oid = C.reltablespace)
--- 101,107 ----
          pg_get_userbyid(C.relowner) AS matviewowner,
          T.spcname AS tablespace,
          C.relhasindex AS hasindexes,
!         C.relisscannable AS isscannable,
          pg_get_viewdef(C.oid) AS definition
      FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
           LEFT JOIN pg_tablespace T ON (T.oid = C.reltablespace)
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index ed62246cc52db0f00dd66b4139c21ef9f7564acf..878b6254f540e613d125823269e1e23f7b99d002 100644
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 30,36 ****
  #include "catalog/objectaccess.h"
  #include "catalog/toasting.h"
  #include "commands/cluster.h"
- #include "commands/matview.h"
  #include "commands/tablecmds.h"
  #include "commands/vacuum.h"
  #include "miscadmin.h"
--- 30,35 ----
*************** cluster_rel(Oid tableOid, Oid indexOid, 
*** 388,394 ****
  	 * database.
  	 */
  	if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
! 		!OldHeap->rd_ispopulated)
  	{
  		relation_close(OldHeap, AccessExclusiveLock);
  		return;
--- 387,393 ----
  	 * database.
  	 */
  	if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
! 		!RelationIsPopulated(OldHeap))
  	{
  		relation_close(OldHeap, AccessExclusiveLock);
  		return;
*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl
*** 922,931 ****
  						get_namespace_name(RelationGetNamespace(OldHeap)),
  						RelationGetRelationName(OldHeap))));
  
- 	if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW)
- 		/* Make sure the heap looks good even if no rows are written. */
- 		SetMatViewToPopulated(NewHeap);
- 
  	/*
  	 * Scan through the OldHeap, either in OldIndex order or sequentially;
  	 * copy each tuple into the NewHeap, or transiently to the tuplesort
--- 921,926 ----
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index de65c4c78174d6c7daa72d49367dfc2bc789df61..14973f8e7c46eb95620a0af49a15b8db504dbd72 100644
*** a/src/backend/commands/createas.c
--- b/src/backend/commands/createas.c
*************** intorel_startup(DestReceiver *self, int 
*** 359,368 ****
  	 */
  	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
  
- 	if (is_matview && !into->skipData)
- 		/* Make sure the heap looks good even if no rows are written. */
- 		SetMatViewToPopulated(intoRelationDesc);
- 
  	/*
  	 * Check INSERT permission on the constructed table.
  	 *
--- 359,364 ----
*************** intorel_startup(DestReceiver *self, int 
*** 382,387 ****
--- 378,390 ----
  	ExecCheckRTPerms(list_make1(rte), true);
  
  	/*
+ 	 * Tentatively mark the target as populated, if it's a matview and we're
+ 	 * going to fill it; otherwise, no change needed.
+ 	 */
+ 	if (is_matview && !into->skipData)
+ 		SetMatViewPopulatedState(intoRelationDesc, true);
+ 
+ 	/*
  	 * Fill private fields of myState for use by later routines
  	 */
  	myState->rel = intoRelationDesc;
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index da373045cc02fd62cade8b5b222e5de7627f3705..390aba112563f3ad5f4663b0aa6a9dc2f2b3a94e 100644
*** a/src/backend/commands/matview.c
--- b/src/backend/commands/matview.c
***************
*** 31,36 ****
--- 31,37 ----
  #include "storage/smgr.h"
  #include "tcop/tcopprot.h"
  #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
  
  
  typedef struct
*************** static void refresh_matview_datafill(Des
*** 52,89 ****
  									 const char *queryString);
  
  /*
!  * SetMatViewToPopulated
!  *		Indicate that the materialized view has been populated by its query.
!  *
!  * NOTE: The heap starts out in a state that doesn't look scannable, and can
!  * only transition from there to scannable at the time a new heap is created.
   *
   * NOTE: caller must be holding an appropriate lock on the relation.
   */
  void
! SetMatViewToPopulated(Relation relation)
  {
! 	Page        page;
  
  	Assert(relation->rd_rel->relkind == RELKIND_MATVIEW);
- 	Assert(relation->rd_ispopulated == false);
  
! 	page = (Page) palloc(BLCKSZ);
! 	PageInit(page, BLCKSZ, 0);
! 
! 	if (RelationNeedsWAL(relation))
! 		log_newpage(&(relation->rd_node), MAIN_FORKNUM, 0, page);
  
! 	RelationOpenSmgr(relation);
  
! 	PageSetChecksumInplace(page, 0);
! 	smgrextend(relation->rd_smgr, MAIN_FORKNUM, 0, (char *) page, true);
  
! 	pfree(page);
  
! 	smgrimmedsync(relation->rd_smgr, MAIN_FORKNUM);
  
! 	RelationCacheInvalidateEntry(relation->rd_id);
  }
  
  /*
--- 53,97 ----
  									 const char *queryString);
  
  /*
!  * SetMatViewPopulatedState
!  *		Mark a materialized view as populated, or not.
   *
   * NOTE: caller must be holding an appropriate lock on the relation.
   */
  void
! SetMatViewPopulatedState(Relation relation, bool newstate)
  {
! 	Relation	pgrel;
! 	HeapTuple	tuple;
  
  	Assert(relation->rd_rel->relkind == RELKIND_MATVIEW);
  
! 	/*
! 	 * Update relation's pg_class entry.  Crucial side-effect: other backends
! 	 * (and this one too!) are sent SI message to make them rebuild relcache
! 	 * entries.
! 	 */
! 	pgrel = heap_open(RelationRelationId, RowExclusiveLock);
! 	tuple = SearchSysCacheCopy1(RELOID,
! 								ObjectIdGetDatum(RelationGetRelid(relation)));
! 	if (!HeapTupleIsValid(tuple))
! 		elog(ERROR, "cache lookup failed for relation %u",
! 			 RelationGetRelid(relation));
  
! 	((Form_pg_class) GETSTRUCT(tuple))->relisscannable = newstate;
  
! 	simple_heap_update(pgrel, &tuple->t_self, tuple);
  
! 	CatalogUpdateIndexes(pgrel, tuple);
  
! 	heap_freetuple(tuple);
! 	heap_close(pgrel, RowExclusiveLock);
  
! 	/*
! 	 * Advance command counter to make the updated pg_class row locally
! 	 * visible.
! 	 */
! 	CommandCounterIncrement();
  }
  
  /*
*************** SetMatViewToPopulated(Relation relation)
*** 97,110 ****
   * If WITH NO DATA was specified, this is effectively like a TRUNCATE;
   * otherwise it is like a TRUNCATE followed by an INSERT using the SELECT
   * statement associated with the materialized view.  The statement node's
!  * skipData field is used to indicate that the clause was used.
   *
   * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
   * the new heap, it's better to create the indexes afterwards than to fill them
   * incrementally while we load.
   *
!  * The scannable state is changed based on whether the contents reflect the
!  * result set of the materialized view's query.
   */
  void
  ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
--- 105,118 ----
   * If WITH NO DATA was specified, this is effectively like a TRUNCATE;
   * otherwise it is like a TRUNCATE followed by an INSERT using the SELECT
   * statement associated with the materialized view.  The statement node's
!  * skipData field shows whether the clause was used.
   *
   * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
   * the new heap, it's better to create the indexes afterwards than to fill them
   * incrementally while we load.
   *
!  * The matview's "populated" state is changed based on whether the contents
!  * reflect the result set of the materialized view's query.
   */
  void
  ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
*************** ExecRefreshMatView(RefreshMatViewStmt *s
*** 184,189 ****
--- 192,203 ----
  	 */
  	CheckTableNotInUse(matviewRel, "REFRESH MATERIALIZED VIEW");
  
+ 	/*
+ 	 * Tentatively mark the matview as populated or not (this will roll back
+ 	 * if we fail later).
+ 	 */
+ 	SetMatViewPopulatedState(matviewRel, !stmt->skipData);
+ 
  	tableSpace = matviewRel->rd_rel->reltablespace;
  
  	heap_close(matviewRel, NoLock);
*************** ExecRefreshMatView(RefreshMatViewStmt *s
*** 192,197 ****
--- 206,212 ----
  	OIDNewHeap = make_new_heap(matviewOid, tableSpace);
  	dest = CreateTransientRelDestReceiver(OIDNewHeap);
  
+ 	/* Generate the data, if wanted. */
  	if (!stmt->skipData)
  		refresh_matview_datafill(dest, dataQuery, queryString);
  
*************** transientrel_startup(DestReceiver *self,
*** 300,307 ****
  		myState->hi_options |= HEAP_INSERT_SKIP_WAL;
  	myState->bistate = GetBulkInsertState();
  
- 	SetMatViewToPopulated(transientrel);
- 
  	/* Not using WAL requires smgr_targblock be initially invalid */
  	Assert(RelationGetTargetBlock(transientrel) == InvalidBlockNumber);
  }
--- 315,320 ----
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 02f3cf3c205483678e1637ac612134c99fd786d6..9d304153b8bee4a4cd02701dc70173cdc06a60e1 100644
*** a/src/backend/commands/vacuumlazy.c
--- b/src/backend/commands/vacuumlazy.c
*************** lazy_vacuum_rel(Relation onerel, VacuumS
*** 230,242 ****
  	 *
  	 * Don't even think about it unless we have a shot at releasing a goodly
  	 * number of pages.  Otherwise, the time taken isn't worth it.
- 	 *
- 	 * Leave a populated materialized view with at least one page.
  	 */
- 	if (onerel->rd_rel->relkind == RELKIND_MATVIEW &&
- 		vacrelstats->nonempty_pages == 0)
- 		vacrelstats->nonempty_pages = 1;
- 
  	possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
  	if (possibly_freeable > 0 &&
  		(possibly_freeable >= REL_TRUNCATE_MINIMUM ||
--- 230,236 ----
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index d32d9014c7ef5f16ee45e0cd8df2ab538e383fcf..4c4e1ed82022553dc9f785b8827eb08c6a9c7a20 100644
*** a/src/backend/utils/adt/dbsize.c
--- b/src/backend/utils/adt/dbsize.c
*************** pg_relation_filepath(PG_FUNCTION_ARGS)
*** 834,863 ****
  
  	PG_RETURN_TEXT_P(cstring_to_text(path));
  }
- 
- 
- /*
-  * Indicate whether a relation is scannable.
-  *
-  * Currently, this is always true except for a materialized view which has not
-  * been populated.  It is expected that other conditions for allowing a
-  * materialized view to be scanned will be added in later releases.
-  */
- Datum
- pg_relation_is_scannable(PG_FUNCTION_ARGS)
- {
- 	Oid			relid;
- 	Relation	relation;
- 	bool		result;
- 
- 	relid = PG_GETARG_OID(0);
- 	relation = try_relation_open(relid, AccessShareLock);
- 
- 	if (relation == NULL)
- 		PG_RETURN_BOOL(false);
- 
- 	result = RelationIsScannable(relation);
- 
- 	relation_close(relation, AccessShareLock);
- 	PG_RETURN_BOOL(result);
- }
--- 834,836 ----
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 670fa8c1667e53024b520f2cc463c3a24d488994..482882bc197eff72974e35018aa955a00d1acaca 100644
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
***************
*** 37,43 ****
  #include "access/transam.h"
  #include "access/xact.h"
  #include "catalog/catalog.h"
- #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/indexing.h"
  #include "catalog/namespace.h"
--- 37,42 ----
*************** RelationBuildDesc(Oid targetRelId, bool 
*** 956,967 ****
  	/* make sure relation is marked as having no open file yet */
  	relation->rd_smgr = NULL;
  
- 	if (relation->rd_rel->relkind == RELKIND_MATVIEW &&
- 		heap_is_matview_init_state(relation))
- 		relation->rd_ispopulated = false;
- 	else
- 		relation->rd_ispopulated = true;
- 
  	/*
  	 * now we can free the memory allocated for pg_class_tuple
  	 */
--- 955,960 ----
*************** formrdesc(const char *relationName, Oid 
*** 1459,1464 ****
--- 1452,1460 ----
  	/* formrdesc is used only for permanent relations */
  	relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
  
+ 	/* ... and they're always scannable, too */
+ 	relation->rd_rel->relisscannable = true;
+ 
  	relation->rd_rel->relpages = 0;
  	relation->rd_rel->reltuples = 0;
  	relation->rd_rel->relallvisible = 0;
*************** formrdesc(const char *relationName, Oid 
*** 1531,1537 ****
  	 * initialize physical addressing information for the relation
  	 */
  	RelationInitPhysicalAddr(relation);
- 	relation->rd_ispopulated = true;
  
  	/*
  	 * initialize the rel-has-index flag, using hardwired knowledge
--- 1527,1532 ----
*************** RelationReloadIndexInfo(Relation relatio
*** 1756,1762 ****
  	heap_freetuple(pg_class_tuple);
  	/* We must recalculate physical address in case it changed */
  	RelationInitPhysicalAddr(relation);
- 	relation->rd_ispopulated = true;
  
  	/*
  	 * For a non-system index, there are fields of the pg_index row that are
--- 1751,1756 ----
*************** RelationClearRelation(Relation relation,
*** 1905,1915 ****
  	if (relation->rd_isnailed)
  	{
  		RelationInitPhysicalAddr(relation);
- 		if (relation->rd_rel->relkind == RELKIND_MATVIEW &&
- 			heap_is_matview_init_state(relation))
- 			relation->rd_ispopulated = false;
- 		else
- 			relation->rd_ispopulated = true;
  
  		if (relation->rd_rel->relkind == RELKIND_INDEX)
  		{
--- 1899,1904 ----
*************** RelationBuildLocalRelation(const char *r
*** 2671,2676 ****
--- 2660,2671 ----
  			break;
  	}
  
+ 	/* if it's a materialized view, it's not scannable initially */
+ 	if (relkind == RELKIND_MATVIEW)
+ 		rel->rd_rel->relisscannable = false;
+ 	else
+ 		rel->rd_rel->relisscannable = true;
+ 
  	/*
  	 * Insert relation physical and logical identifiers (OIDs) into the right
  	 * places.	For a mapped relation, we set relfilenode to zero and rely on
*************** RelationBuildLocalRelation(const char *r
*** 2698,2709 ****
  
  	RelationInitPhysicalAddr(rel);
  
- 	/* materialized view not initially scannable */
- 	if (relkind == RELKIND_MATVIEW)
- 		rel->rd_ispopulated = false;
- 	else
- 		rel->rd_ispopulated = true;
- 
  	/*
  	 * Okay to insert into the relcache hash tables.
  	 */
--- 2693,2698 ----
*************** load_relcache_init_file(bool shared)
*** 4448,4458 ****
  		 */
  		RelationInitLockInfo(rel);
  		RelationInitPhysicalAddr(rel);
- 		if (rel->rd_rel->relkind == RELKIND_MATVIEW &&
- 			heap_is_matview_init_state(rel))
- 			rel->rd_ispopulated = false;
- 		else
- 			rel->rd_ispopulated = true;
  	}
  
  	/*
--- 4437,4442 ----
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 007b0865eb506fc8a8263974c09cc44b7986ac71..9db6cc9e35ff8d3c2d5ac416151a5112b6ff81af 100644
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** refreshMatViewData(Archive *fout, TableD
*** 1759,1765 ****
  	PQExpBuffer q;
  
  	/* If the materialized view is not flagged as scannable, skip this. */
! 	if (!tbinfo->isscannable)
  		return;
  
  	q = createPQExpBuffer();
--- 1759,1765 ----
  	PQExpBuffer q;
  
  	/* If the materialized view is not flagged as scannable, skip this. */
! 	if (!tbinfo->relisscannable)
  		return;
  
  	q = createPQExpBuffer();
*************** buildMatViewRefreshDependencies(Archive 
*** 1966,1973 ****
  
  		addObjectDependency(dobj, refdobj->dumpId);
  
! 		if (!reftbinfo->isscannable)
! 			tbinfo->isscannable = false;
  	}
  
  	PQclear(res);
--- 1966,1973 ----
  
  		addObjectDependency(dobj, refdobj->dumpId);
  
! 		if (!reftbinfo->relisscannable)
! 			tbinfo->relisscannable = false;
  	}
  
  	PQclear(res);
*************** getTables(Archive *fout, int *numTables)
*** 4218,4224 ****
  	int			i_toastoid;
  	int			i_toastfrozenxid;
  	int			i_relpersistence;
! 	int			i_isscannable;
  	int			i_owning_tab;
  	int			i_owning_col;
  	int			i_reltablespace;
--- 4218,4224 ----
  	int			i_toastoid;
  	int			i_toastfrozenxid;
  	int			i_relpersistence;
! 	int			i_relisscannable;
  	int			i_owning_tab;
  	int			i_owning_col;
  	int			i_reltablespace;
*************** getTables(Archive *fout, int *numTables)
*** 4264,4271 ****
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "c.relpersistence, "
! 						  "CASE WHEN c.relkind = '%c' THEN pg_relation_is_scannable(c.oid) ELSE 't'::bool END as isscannable, "
  						  "c.relpages, "
  						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
  						  "d.refobjid AS owning_tab, "
--- 4264,4270 ----
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "c.relpersistence, c.relisscannable, "
  						  "c.relpages, "
  						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
  						  "d.refobjid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4283,4289 ****
  				   "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
  						  "ORDER BY c.oid",
  						  username_subquery,
- 						  RELKIND_MATVIEW,
  						  RELKIND_SEQUENCE,
  						  RELKIND_RELATION, RELKIND_SEQUENCE,
  						  RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
--- 4282,4287 ----
*************** getTables(Archive *fout, int *numTables)
*** 4303,4309 ****
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "c.relpersistence, 't'::bool as isscannable, "
  						  "c.relpages, "
  						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
  						  "d.refobjid AS owning_tab, "
--- 4301,4307 ----
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "c.relpersistence, 't' as relisscannable, "
  						  "c.relpages, "
  						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
  						  "d.refobjid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4340,4346 ****
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "c.relpages, "
  						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
  						  "d.refobjid AS owning_tab, "
--- 4338,4344 ----
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "c.relpages, "
  						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
  						  "d.refobjid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4376,4382 ****
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "c.relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
--- 4374,4380 ----
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "c.relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4412,4418 ****
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "c.relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
--- 4410,4416 ----
  						  "c.relhasindex, c.relhasrules, c.relhasoids, "
  						  "c.relfrozenxid, tc.oid AS toid, "
  						  "tc.relfrozenxid AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "c.relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4449,4455 ****
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
--- 4447,4453 ----
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4485,4491 ****
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
--- 4483,4489 ----
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "d.refobjid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4517,4523 ****
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "NULL::oid AS owning_tab, "
--- 4515,4521 ----
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "NULL::oid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4544,4550 ****
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "NULL::oid AS owning_tab, "
--- 4542,4548 ----
  						  "0 AS relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "relpages, "
  						  "NULL AS reloftype, "
  						  "NULL::oid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4581,4587 ****
  						  "0 as relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't'::bool as isscannable, "
  						  "0 AS relpages, "
  						  "NULL AS reloftype, "
  						  "NULL::oid AS owning_tab, "
--- 4579,4585 ----
  						  "0 as relfrozenxid, "
  						  "0 AS toid, "
  						  "0 AS tfrozenxid, "
! 						  "'p' AS relpersistence, 't' as relisscannable, "
  						  "0 AS relpages, "
  						  "NULL AS reloftype, "
  						  "NULL::oid AS owning_tab, "
*************** getTables(Archive *fout, int *numTables)
*** 4630,4636 ****
  	i_toastoid = PQfnumber(res, "toid");
  	i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
  	i_relpersistence = PQfnumber(res, "relpersistence");
! 	i_isscannable = PQfnumber(res, "isscannable");
  	i_relpages = PQfnumber(res, "relpages");
  	i_owning_tab = PQfnumber(res, "owning_tab");
  	i_owning_col = PQfnumber(res, "owning_col");
--- 4628,4634 ----
  	i_toastoid = PQfnumber(res, "toid");
  	i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
  	i_relpersistence = PQfnumber(res, "relpersistence");
! 	i_relisscannable = PQfnumber(res, "relisscannable");
  	i_relpages = PQfnumber(res, "relpages");
  	i_owning_tab = PQfnumber(res, "owning_tab");
  	i_owning_col = PQfnumber(res, "owning_col");
*************** getTables(Archive *fout, int *numTables)
*** 4673,4679 ****
  		tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
  		tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
  		tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
! 		tblinfo[i].isscannable = (strcmp(PQgetvalue(res, i, i_isscannable), "t") == 0);
  		tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
  		tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
  		tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
--- 4671,4677 ----
  		tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
  		tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
  		tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
! 		tblinfo[i].relisscannable = (strcmp(PQgetvalue(res, i, i_relisscannable), "t") == 0);
  		tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
  		tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
  		tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
*************** dumpTableSchema(Archive *fout, TableInfo
*** 13100,13105 ****
--- 13098,13104 ----
  
  		/*
  		 * For materialized views, create the AS clause just like a view.
+ 		 * At this point, we always mark the view as not populated.
  		 */
  		if (tbinfo->relkind == RELKIND_MATVIEW)
  		{
*************** dumpTableSchema(Archive *fout, TableInfo
*** 13229,13234 ****
--- 13228,13250 ----
  		}
  
  		/*
+ 		 * In binary_upgrade mode, restore matviews' scannability status by
+ 		 * poking pg_class directly.  This is pretty ugly, but we can't use
+ 		 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
+ 		 * matview is not populated even though this matview is.
+ 		 */
+ 		if (binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
+ 			tbinfo->relisscannable)
+ 		{
+ 			appendPQExpBuffer(q, "\n-- For binary upgrade, mark materialized view as scannable\n");
+ 			appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
+ 							  "SET relisscannable = 't'\n"
+ 							  "WHERE oid = ");
+ 			appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout);
+ 			appendPQExpBuffer(q, "::pg_catalog.regclass;\n");
+ 		}
+ 
+ 		/*
  		 * Dump additional per-column properties that we can't handle in the
  		 * main CREATE TABLE command.
  		 */
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7970a359bd80ceec7bca4aa62cb625c84a351bf6..8e761afcf46d21f9e8e1fb1fdf66932bd5add393 100644
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef struct _tableInfo
*** 236,241 ****
--- 236,242 ----
  	char	   *relacl;
  	char		relkind;
  	char		relpersistence; /* relation persistence */
+ 	bool		relisscannable;	/* is valid for use in queries */
  	char	   *reltablespace;	/* relation tablespace */
  	char	   *reloptions;		/* options specified by WITH (...) */
  	char	   *toast_reloptions;		/* ditto, for the TOAST table */
*************** typedef struct _tableInfo
*** 243,249 ****
  	bool		hasrules;		/* does it have any rules? */
  	bool		hastriggers;	/* does it have any triggers? */
  	bool		hasoids;		/* does it have OIDs? */
- 	bool		isscannable;	/* is valid for use in queries */
  	uint32		frozenxid;		/* for restore frozen xid */
  	Oid			toast_oid;		/* for restore toast frozen xid */
  	uint32		toast_frozenxid;	/* for restore toast frozen xid */
--- 244,249 ----
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 97d507e4c2321fbd3c35fd6a2ff58fadc99f33df..6b60d55a362df9027c7022f24dd0565814c0ba8a 100644
*** a/src/include/catalog/heap.h
--- b/src/include/catalog/heap.h
*************** extern Oid heap_create_with_catalog(cons
*** 70,76 ****
  						 bool is_internal);
  
  extern void heap_create_init_fork(Relation rel);
- extern bool heap_is_matview_init_state(Relation rel);
  
  extern void heap_drop_with_catalog(Oid relid);
  
--- 70,75 ----
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index fd97141e9ef489431c7fc3600c6e19e81712da11..fb68d6f91cbdcb0fa8a8a32fd0b2896f7caced1e 100644
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
*************** CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI
*** 66,71 ****
--- 66,72 ----
  	bool		relhasrules;	/* has (or has had) any rules */
  	bool		relhastriggers; /* has (or has had) any TRIGGERs */
  	bool		relhassubclass; /* has (or has had) derived classes */
+ 	bool		relisscannable;	/* T if okay to read contents of relation */
  	TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
  	TransactionId relminmxid;	/* all multixacts in this rel are >= this.
  								 * this is really a MultiXactId */
*************** typedef FormData_pg_class *Form_pg_class
*** 93,99 ****
   * ----------------
   */
  
! #define Natts_pg_class					28
  #define Anum_pg_class_relname			1
  #define Anum_pg_class_relnamespace		2
  #define Anum_pg_class_reltype			3
--- 94,100 ----
   * ----------------
   */
  
! #define Natts_pg_class					29
  #define Anum_pg_class_relname			1
  #define Anum_pg_class_relnamespace		2
  #define Anum_pg_class_reltype			3
*************** typedef FormData_pg_class *Form_pg_class
*** 118,127 ****
  #define Anum_pg_class_relhasrules		22
  #define Anum_pg_class_relhastriggers	23
  #define Anum_pg_class_relhassubclass	24
! #define Anum_pg_class_relfrozenxid		25
! #define Anum_pg_class_relminmxid		26
! #define Anum_pg_class_relacl			27
! #define Anum_pg_class_reloptions		28
  
  /* ----------------
   *		initial contents of pg_class
--- 119,129 ----
  #define Anum_pg_class_relhasrules		22
  #define Anum_pg_class_relhastriggers	23
  #define Anum_pg_class_relhassubclass	24
! #define Anum_pg_class_relisscannable	25
! #define Anum_pg_class_relfrozenxid		26
! #define Anum_pg_class_relminmxid		27
! #define Anum_pg_class_relacl			28
! #define Anum_pg_class_reloptions		29
  
  /* ----------------
   *		initial contents of pg_class
*************** typedef FormData_pg_class *Form_pg_class
*** 136,148 ****
   * Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
   * similarly, "1" in relminmxid stands for FirstMultiXactId
   */
! DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 28 0 t f f f f 3 1 _null_ _null_ ));
  DESCR("");
  
  
--- 138,150 ----
   * Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
   * similarly, "1" in relminmxid stands for FirstMultiXactId
   */
! DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 29 0 t f f f f t 3 1 _null_ _null_ ));
  DESCR("");
  
  
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ef892978ffbabf891219394d9c796cd2a243d751..685b9c76cfac4b006dea50fdd874881691bc7da2 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 3842 (  pg_view_is_ins
*** 1980,1987 ****
  DESCR("is a view insertable-into");
  DATA(insert OID = 3843 (  pg_view_is_updatable	PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_view_is_updatable _null_ _null_ _null_ ));
  DESCR("is a view updatable");
- DATA(insert OID = 3846 (  pg_relation_is_scannable	PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_relation_is_scannable _null_ _null_ _null_ ));
- DESCR("is a relation scannable");
  
  /* Deferrable unique constraint trigger */
  DATA(insert OID = 1250 (  unique_key_recheck	PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 2279 "" _null_ _null_ _null_ _null_ unique_key_recheck _null_ _null_ _null_ ));
--- 1980,1985 ----
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 09bc384086fa1054b615e4f7c01dcb0eb125ab68..e3ce2f2953139b850aa93da6aa34d5c0bde360e6 100644
*** a/src/include/commands/matview.h
--- b/src/include/commands/matview.h
***************
*** 20,26 ****
  #include "utils/relcache.h"
  
  
! extern void SetMatViewToPopulated(Relation relation);
  
  extern void ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
  				  ParamListInfo params, char *completionTag);
--- 20,26 ----
  #include "utils/relcache.h"
  
  
! extern void SetMatViewPopulatedState(Relation relation, bool newstate);
  
  extern void ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
  				  ParamListInfo params, char *completionTag);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index e71876502e1e6e56aab6066ee7388383ce9c0a59..15b60abfcd9360730ff86fe85a6918599e5546e3 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_table_size(PG_FUNCTION_A
*** 461,467 ****
  extern Datum pg_indexes_size(PG_FUNCTION_ARGS);
  extern Datum pg_relation_filenode(PG_FUNCTION_ARGS);
  extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
- extern Datum pg_relation_is_scannable(PG_FUNCTION_ARGS);
  
  /* genfile.c */
  extern bytea *read_binary_file(const char *filename,
--- 461,466 ----
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 632743af9436e14f8b537e58d2fc0acbbb319873..67cb11193aeb660e869eb66ef36077fac67e9a7f 100644
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
*************** typedef struct RelationData
*** 77,83 ****
  	BackendId	rd_backend;		/* owning backend id, if temporary relation */
  	bool		rd_islocaltemp; /* rel is a temp rel of this session */
  	bool		rd_isnailed;	/* rel is nailed in cache */
- 	bool		rd_ispopulated;	/* matview has query results */
  	bool		rd_isvalid;		/* relcache entry is valid */
  	char		rd_indexvalid;	/* state of rd_indexlist: 0 = not valid, 1 =
  								 * valid, 2 = temporarily forced */
--- 77,82 ----
*************** typedef struct StdRdOptions
*** 408,414 ****
   * 		populated by its query.  This is likely to get more complicated later,
   * 		so use a macro which looks like a function.
   */
! #define RelationIsScannable(relation) ((relation)->rd_ispopulated)
  
  
  /* routines in utils/cache/relcache.c */
--- 407,421 ----
   * 		populated by its query.  This is likely to get more complicated later,
   * 		so use a macro which looks like a function.
   */
! #define RelationIsScannable(relation) ((relation)->rd_rel->relisscannable)
! 
! /*
!  * RelationIsPopulated
!  * 		Currently, we don't physically distinguish the "populated" and
!  *		"scannable" properties of matviews, but that may change later.
!  *		Hence, use the appropriate one of these macros in code tests.
!  */
! #define RelationIsPopulated(relation) ((relation)->rd_rel->relisscannable)
  
  
  /* routines in utils/cache/relcache.c */
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 06bb2551a83480361ddfce5ad02b76240d9eaf63..87da43e85c777ca9d864be92e409616599a408e9 100644
*** a/src/test/regress/expected/matview.out
--- b/src/test/regress/expected/matview.out
*************** EXPLAIN (costs off)
*** 26,34 ****
  (2 rows)
  
  CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
! SELECT pg_relation_is_scannable('tm'::regclass);
!  pg_relation_is_scannable 
! --------------------------
   f
  (1 row)
  
--- 26,34 ----
  (2 rows)
  
  CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
! SELECT relisscannable FROM pg_class WHERE oid = 'tm'::regclass;
!  relisscannable 
! ----------------
   f
  (1 row)
  
*************** SELECT * FROM tm;
*** 36,44 ****
  ERROR:  materialized view "tm" has not been populated
  HINT:  Use the REFRESH MATERIALIZED VIEW command.
  REFRESH MATERIALIZED VIEW tm;
! SELECT pg_relation_is_scannable('tm'::regclass);
!  pg_relation_is_scannable 
! --------------------------
   t
  (1 row)
  
--- 36,44 ----
  ERROR:  materialized view "tm" has not been populated
  HINT:  Use the REFRESH MATERIALIZED VIEW command.
  REFRESH MATERIALIZED VIEW tm;
! SELECT relisscannable FROM pg_class WHERE oid = 'tm'::regclass;
!  relisscannable 
! ----------------
   t
  (1 row)
  
*************** UNION ALL 
*** 354,362 ****
             FROM v_test2;
  
  CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345;
! SELECT pg_relation_is_scannable('mv_test3'::regclass);
!  pg_relation_is_scannable 
! --------------------------
   t
  (1 row)
  
--- 354,362 ----
             FROM v_test2;
  
  CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345;
! SELECT relisscannable FROM pg_class WHERE oid = 'mv_test3'::regclass;
!  relisscannable 
! ----------------
   t
  (1 row)
  
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a4ecfd2aeac36ba5137a3d17592a9b0751cbb201..b710ec62f46d806c04683eb33f386bb53eeda92c 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** SELECT viewname, definition FROM pg_view
*** 1347,1353 ****
                                   |     pg_get_userbyid(c.relowner) AS matviewowner,                                                                                                                                                               +
                                   |     t.spcname AS tablespace,                                                                                                                                                                                   +
                                   |     c.relhasindex AS hasindexes,                                                                                                                                                                               +
!                                  |     pg_relation_is_scannable(c.oid) AS isscannable,                                                                                                                                                            +
                                   |     pg_get_viewdef(c.oid) AS definition                                                                                                                                                                        +
                                   |    FROM ((pg_class c                                                                                                                                                                                           +
                                   |    LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))                                                                                                                                                     +
--- 1347,1353 ----
                                   |     pg_get_userbyid(c.relowner) AS matviewowner,                                                                                                                                                               +
                                   |     t.spcname AS tablespace,                                                                                                                                                                                   +
                                   |     c.relhasindex AS hasindexes,                                                                                                                                                                               +
!                                  |     c.relisscannable AS isscannable,                                                                                                                                                                           +
                                   |     pg_get_viewdef(c.oid) AS definition                                                                                                                                                                        +
                                   |    FROM ((pg_class c                                                                                                                                                                                           +
                                   |    LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))                                                                                                                                                     +
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index 09a7378133c86d66755dafd1629719a712badfdf..84b1af004288a2af9f7e583feed0fb424725051b 100644
*** a/src/test/regress/sql/matview.sql
--- b/src/test/regress/sql/matview.sql
*************** SELECT * FROM tv ORDER BY type;
*** 15,24 ****
  EXPLAIN (costs off)
    CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
  CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
! SELECT pg_relation_is_scannable('tm'::regclass);
  SELECT * FROM tm;
  REFRESH MATERIALIZED VIEW tm;
! SELECT pg_relation_is_scannable('tm'::regclass);
  CREATE UNIQUE INDEX tm_type ON tm (type);
  SELECT * FROM tm;
  
--- 15,24 ----
  EXPLAIN (costs off)
    CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
  CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
! SELECT relisscannable FROM pg_class WHERE oid = 'tm'::regclass;
  SELECT * FROM tm;
  REFRESH MATERIALIZED VIEW tm;
! SELECT relisscannable FROM pg_class WHERE oid = 'tm'::regclass;
  CREATE UNIQUE INDEX tm_type ON tm (type);
  SELECT * FROM tm;
  
*************** CREATE VIEW v_test2 AS SELECT moo, 2*moo
*** 109,115 ****
  CREATE MATERIALIZED VIEW mv_test2 AS SELECT moo, 2*moo FROM v_test2 UNION ALL SELECT moo, 3*moo FROM v_test2;
  \d+ mv_test2
  CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345;
! SELECT pg_relation_is_scannable('mv_test3'::regclass);
  
  DROP VIEW v_test1 CASCADE;
  
--- 109,115 ----
  CREATE MATERIALIZED VIEW mv_test2 AS SELECT moo, 2*moo FROM v_test2 UNION ALL SELECT moo, 3*moo FROM v_test2;
  \d+ mv_test2
  CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345;
! SELECT relisscannable FROM pg_class WHERE oid = 'mv_test3'::regclass;
  
  DROP VIEW v_test1 CASCADE;
  
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to