diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 6c0ef5bd195bac55e1b3b799d49e60ccda83738a..ec5232c91a09288d29a9a8704e7c11cc3d9de4c5 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1863,6 +1863,14 @@
       <entry>True if table has (or once had) any inheritance children</entry>
      </row>
 
+     <row>
+      <entry><structfield>relispopulated</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>True if relation is populated (this is true for all
+       relations other than some materialized views)</entry>
+     </row>
+
      <row>
       <entry><structfield>relfrozenxid</structfield></entry>
       <entry><type>xid</type></entry>
@@ -7776,14 +7784,14 @@
      <row>
       <entry><structfield>hasindexes</structfield></entry>
       <entry><type>boolean</type></entry>
-      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relhasindex</literal></entry>
+      <entry></entry>
       <entry>True if materialized view has (or recently had) any indexes</entry>
      </row>
      <row>
-      <entry><structfield>isscannable</structfield></entry>
+      <entry><structfield>ispopulated</structfield></entry>
       <entry><type>boolean</type></entry>
       <entry></entry>
-      <entry>True if materialized view can currently be scanned</entry>
+      <entry>True if materialized view is currently populated</entry>
      </row>
      <row>
       <entry><structfield>definition</structfield></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
@@ -14238,10 +14238,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
     <primary>pg_tablespace_location</primary>
    </indexterm>
 
-   <indexterm>
-    <primary>pg_relation_is_scannable</primary>
-   </indexterm>
-
    <indexterm>
     <primary>pg_typeof</primary>
    </indexterm>
@@ -14410,11 +14406,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
        <entry><type>text</type></entry>
        <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>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0b4c659ff0751a48e69c4b7886a7ba38804eb46a..24a8474cb51b212434836eea45f57d13c1492d70 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -780,6 +780,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 	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_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
 	values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
 	values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid);
 	if (relacl != (Datum) 0)
@@ -1345,26 +1346,6 @@ heap_create_init_fork(Relation rel)
 	smgrimmedsync(rel->rd_smgr, INIT_FORKNUM);
 }
 
-/*
- * 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
  *
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 57adbf607de1a866727e905c8387c7a4618b4237..a03bfa684bf2078ea0aad60edbe665dbf0198841 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -101,7 +101,7 @@ CREATE VIEW pg_matviews AS
         pg_get_userbyid(C.relowner) AS matviewowner,
         T.spcname AS tablespace,
         C.relhasindex AS hasindexes,
-        pg_relation_is_scannable(C.oid) AS isscannable,
+        C.relispopulated AS ispopulated,
         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,7 +30,6 @@
 #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"
@@ -388,7 +387,7 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
 	 * database.
 	 */
 	if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
-		!OldHeap->rd_ispopulated)
+		!RelationIsPopulated(OldHeap))
 	{
 		relation_close(OldHeap, AccessExclusiveLock);
 		return;
@@ -922,10 +921,6 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
 						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
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
@@ -359,10 +359,6 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	 */
 	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.
 	 *
@@ -381,6 +377,13 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 
 	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
 	 */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index da373045cc02fd62cade8b5b222e5de7627f3705..5491c84c7660c8691da4139d2a9f9d89b38e5f39 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -14,12 +14,11 @@
  */
 #include "postgres.h"
 
-#include "access/heapam_xlog.h"
+#include "access/htup_details.h"
 #include "access/multixact.h"
-#include "access/relscan.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
-#include "catalog/heap.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "commands/cluster.h"
 #include "commands/matview.h"
@@ -27,10 +26,11 @@
 #include "executor/executor.h"
 #include "miscadmin.h"
 #include "rewrite/rewriteHandler.h"
-#include "storage/lmgr.h"
 #include "storage/smgr.h"
 #include "tcop/tcopprot.h"
+#include "utils/rel.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 
 
 typedef struct
@@ -52,38 +52,45 @@ static void refresh_matview_datafill(DestReceiver *dest, Query *query,
 									 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.
+ * SetMatViewPopulatedState
+ *		Mark a materialized view as populated, or not.
  *
  * NOTE: caller must be holding an appropriate lock on the relation.
  */
 void
-SetMatViewToPopulated(Relation relation)
+SetMatViewPopulatedState(Relation relation, bool newstate)
 {
-	Page        page;
+	Relation	pgrel;
+	HeapTuple	tuple;
 
 	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);
+	/*
+	 * 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));
 
-	RelationOpenSmgr(relation);
+	((Form_pg_class) GETSTRUCT(tuple))->relispopulated = newstate;
 
-	PageSetChecksumInplace(page, 0);
-	smgrextend(relation->rd_smgr, MAIN_FORKNUM, 0, (char *) page, true);
+	simple_heap_update(pgrel, &tuple->t_self, tuple);
 
-	pfree(page);
+	CatalogUpdateIndexes(pgrel, tuple);
 
-	smgrimmedsync(relation->rd_smgr, MAIN_FORKNUM);
+	heap_freetuple(tuple);
+	heap_close(pgrel, RowExclusiveLock);
 
-	RelationCacheInvalidateEntry(relation->rd_id);
+	/*
+	 * Advance command counter to make the updated pg_class row locally
+	 * visible.
+	 */
+	CommandCounterIncrement();
 }
 
 /*
@@ -97,14 +104,14 @@ SetMatViewToPopulated(Relation relation)
  * 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.
+ * 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 scannable state is changed based on whether the contents reflect the
- * result set of the materialized view's query.
+ * 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,
@@ -184,6 +191,12 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
 	 */
 	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);
@@ -192,6 +205,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
 	OIDNewHeap = make_new_heap(matviewOid, tableSpace);
 	dest = CreateTransientRelDestReceiver(OIDNewHeap);
 
+	/* Generate the data, if wanted. */
 	if (!stmt->skipData)
 		refresh_matview_datafill(dest, dataQuery, queryString);
 
@@ -300,8 +314,6 @@ transientrel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 		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);
 }
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
@@ -230,13 +230,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 	 *
 	 * 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 ||
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
@@ -834,30 +834,3 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 
 	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);
-}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 670fa8c1667e53024b520f2cc463c3a24d488994..7888d387234662f87f68983ad253d439e7a2e443 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -37,7 +37,6 @@
 #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"
@@ -956,12 +955,6 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	/* 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
 	 */
@@ -1459,6 +1452,9 @@ formrdesc(const char *relationName, Oid relationReltype,
 	/* formrdesc is used only for permanent relations */
 	relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
 
+	/* ... and they're always populated, too */
+	relation->rd_rel->relispopulated = true;
+
 	relation->rd_rel->relpages = 0;
 	relation->rd_rel->reltuples = 0;
 	relation->rd_rel->relallvisible = 0;
@@ -1531,7 +1527,6 @@ formrdesc(const char *relationName, Oid relationReltype,
 	 * initialize physical addressing information for the relation
 	 */
 	RelationInitPhysicalAddr(relation);
-	relation->rd_ispopulated = true;
 
 	/*
 	 * initialize the rel-has-index flag, using hardwired knowledge
@@ -1756,7 +1751,6 @@ RelationReloadIndexInfo(Relation relation)
 	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
@@ -1905,11 +1899,6 @@ RelationClearRelation(Relation relation, bool rebuild)
 	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)
 		{
@@ -2671,6 +2660,12 @@ RelationBuildLocalRelation(const char *relname,
 			break;
 	}
 
+	/* if it's a materialized view, it's not populated initially */
+	if (relkind == RELKIND_MATVIEW)
+		rel->rd_rel->relispopulated = false;
+	else
+		rel->rd_rel->relispopulated = true;
+
 	/*
 	 * Insert relation physical and logical identifiers (OIDs) into the right
 	 * places.	For a mapped relation, we set relfilenode to zero and rely on
@@ -2698,12 +2693,6 @@ RelationBuildLocalRelation(const char *relname,
 
 	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.
 	 */
@@ -4448,11 +4437,6 @@ load_relcache_init_file(bool shared)
 		 */
 		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;
 	}
 
 	/*
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9ecfd0c6aacc31a7423dd0a441986cc832bb94e9..76628fcbc39be7531141a6a37d4226745e607b88 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1759,8 +1759,8 @@ refreshMatViewData(Archive *fout, TableDataInfo *tdinfo)
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PQExpBuffer q;
 
-	/* If the materialized view is not flagged as scannable, skip this. */
-	if (!tbinfo->isscannable)
+	/* If the materialized view is not flagged as populated, skip this. */
+	if (!tbinfo->relispopulated)
 		return;
 
 	q = createPQExpBuffer();
@@ -1967,8 +1967,8 @@ buildMatViewRefreshDependencies(Archive *fout)
 
 		addObjectDependency(dobj, refdobj->dumpId);
 
-		if (!reftbinfo->isscannable)
-			tbinfo->isscannable = false;
+		if (!reftbinfo->relispopulated)
+			tbinfo->relispopulated = false;
 	}
 
 	PQclear(res);
@@ -4219,7 +4219,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_toastoid;
 	int			i_toastfrozenxid;
 	int			i_relpersistence;
-	int			i_isscannable;
+	int			i_relispopulated;
 	int			i_owning_tab;
 	int			i_owning_col;
 	int			i_reltablespace;
@@ -4265,8 +4265,7 @@ getTables(Archive *fout, int *numTables)
 						  "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.relpersistence, c.relispopulated, "
 						  "c.relpages, "
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
@@ -4284,7 +4283,6 @@ getTables(Archive *fout, int *numTables)
 				   "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,
@@ -4304,7 +4302,7 @@ getTables(Archive *fout, int *numTables)
 						  "c.relhasindex, c.relhasrules, c.relhasoids, "
 						  "c.relfrozenxid, tc.oid AS toid, "
 						  "tc.relfrozenxid AS tfrozenxid, "
-						  "c.relpersistence, 't'::bool as isscannable, "
+						  "c.relpersistence, 't' as relispopulated, "
 						  "c.relpages, "
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
@@ -4341,7 +4339,7 @@ getTables(Archive *fout, int *numTables)
 						  "c.relhasindex, c.relhasrules, c.relhasoids, "
 						  "c.relfrozenxid, tc.oid AS toid, "
 						  "tc.relfrozenxid AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "c.relpages, "
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
@@ -4377,7 +4375,7 @@ getTables(Archive *fout, int *numTables)
 						  "c.relhasindex, c.relhasrules, c.relhasoids, "
 						  "c.relfrozenxid, tc.oid AS toid, "
 						  "tc.relfrozenxid AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "c.relpages, "
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
@@ -4413,7 +4411,7 @@ getTables(Archive *fout, int *numTables)
 						  "c.relhasindex, c.relhasrules, c.relhasoids, "
 						  "c.relfrozenxid, tc.oid AS toid, "
 						  "tc.relfrozenxid AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "c.relpages, "
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
@@ -4450,7 +4448,7 @@ getTables(Archive *fout, int *numTables)
 						  "0 AS relfrozenxid, "
 						  "0 AS toid, "
 						  "0 AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "relpages, "
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
@@ -4486,7 +4484,7 @@ getTables(Archive *fout, int *numTables)
 						  "0 AS relfrozenxid, "
 						  "0 AS toid, "
 						  "0 AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "relpages, "
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
@@ -4518,7 +4516,7 @@ getTables(Archive *fout, int *numTables)
 						  "0 AS relfrozenxid, "
 						  "0 AS toid, "
 						  "0 AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "relpages, "
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
@@ -4545,7 +4543,7 @@ getTables(Archive *fout, int *numTables)
 						  "0 AS relfrozenxid, "
 						  "0 AS toid, "
 						  "0 AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "relpages, "
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
@@ -4582,7 +4580,7 @@ getTables(Archive *fout, int *numTables)
 						  "0 as relfrozenxid, "
 						  "0 AS toid, "
 						  "0 AS tfrozenxid, "
-						  "'p' AS relpersistence, 't'::bool as isscannable, "
+						  "'p' AS relpersistence, 't' as relispopulated, "
 						  "0 AS relpages, "
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
@@ -4631,7 +4629,7 @@ getTables(Archive *fout, int *numTables)
 	i_toastoid = PQfnumber(res, "toid");
 	i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
 	i_relpersistence = PQfnumber(res, "relpersistence");
-	i_isscannable = PQfnumber(res, "isscannable");
+	i_relispopulated = PQfnumber(res, "relispopulated");
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
@@ -4674,7 +4672,7 @@ getTables(Archive *fout, int *numTables)
 		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].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "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));
@@ -13101,6 +13099,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 
 		/*
 		 * 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)
 		{
@@ -13229,6 +13228,23 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 			}
 		}
 
+		/*
+		 * In binary_upgrade mode, restore matviews' populated 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->relispopulated)
+		{
+			appendPQExpBuffer(q, "\n-- For binary upgrade, mark materialized view as populated\n");
+			appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
+							  "SET relispopulated = '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..6352adaa05e8b33f6d67ae5f0e5926d89200d4d0 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -236,6 +236,7 @@ typedef struct _tableInfo
 	char	   *relacl;
 	char		relkind;
 	char		relpersistence; /* relation persistence */
+	bool		relispopulated;	/* relation is populated */
 	char	   *reltablespace;	/* relation tablespace */
 	char	   *reloptions;		/* options specified by WITH (...) */
 	char	   *toast_reloptions;		/* ditto, for the TOAST table */
@@ -243,7 +244,6 @@ typedef struct _tableInfo
 	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 */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 42eb4de279431445ed7019f491d716548701226f..392649c37e652a6d70759dc71b3a7a7c663e2d10 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201304271
+#define CATALOG_VERSION_NO	201305061
 
 #endif
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
@@ -70,7 +70,6 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 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);
 
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index fd97141e9ef489431c7fc3600c6e19e81712da11..0f7ad5d743661e5cd200e1b1b5aee2c33d860128 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -66,6 +66,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
 	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		relispopulated;	/* matview currently holds query results */
 	TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
 	TransactionId relminmxid;	/* all multixacts in this rel are >= this.
 								 * this is really a MultiXactId */
@@ -93,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class;
  * ----------------
  */
 
-#define Natts_pg_class					28
+#define Natts_pg_class					29
 #define Anum_pg_class_relname			1
 #define Anum_pg_class_relnamespace		2
 #define Anum_pg_class_reltype			3
@@ -118,10 +119,11 @@ typedef FormData_pg_class *Form_pg_class;
 #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
+#define Anum_pg_class_relispopulated	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
@@ -136,13 +138,13 @@ typedef FormData_pg_class *Form_pg_class;
  * 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_ ));
+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 3 1 _null_ _null_ ));
+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 3 1 _null_ _null_ ));
+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 28 0 t f f f f 3 1 _null_ _null_ ));
+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
@@ -1980,8 +1980,6 @@ DATA(insert OID = 3842 (  pg_view_is_insertable PGNSP PGUID 12 10 0 0 0 f f f f
 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_ ));
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,7 +20,7 @@
 #include "utils/relcache.h"
 
 
-extern void SetMatViewToPopulated(Relation relation);
+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
@@ -461,7 +461,6 @@ extern Datum pg_table_size(PG_FUNCTION_ARGS);
 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,
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 632743af9436e14f8b537e58d2fc0acbbb319873..4b833c5018c4c82f554fb8bf230fad572dc8dfd7 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -77,7 +77,6 @@ typedef struct RelationData
 	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 */
@@ -408,7 +407,15 @@ typedef struct StdRdOptions
  * 		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)
+#define RelationIsScannable(relation) ((relation)->rd_rel->relispopulated)
+
+/*
+ * 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->relispopulated)
 
 
 /* routines in utils/cache/relcache.c */
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 06bb2551a83480361ddfce5ad02b76240d9eaf63..a98de4f58d332d93e2088be5716d817e4f5ac713 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -26,9 +26,9 @@ EXPLAIN (costs off)
 (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 
---------------------------
+SELECT relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
+ relispopulated 
+----------------
  f
 (1 row)
 
@@ -36,9 +36,9 @@ SELECT * FROM tm;
 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 
---------------------------
+SELECT relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
+ relispopulated 
+----------------
  t
 (1 row)
 
@@ -354,9 +354,9 @@ UNION ALL
            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 
---------------------------
+SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass;
+ relispopulated 
+----------------
  t
 (1 row)
 
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a4ecfd2aeac36ba5137a3d17592a9b0751cbb201..dc3c8a83812f80c96410060d507e2b0f664d84df 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1347,7 +1347,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
                                  |     pg_get_userbyid(c.relowner) AS matviewowner,                                                                                                                                                               +
                                  |     t.spcname AS tablespace,                                                                                                                                                                                   +
                                  |     c.relhasindex AS hasindexes,                                                                                                                                                                               +
-                                 |     pg_relation_is_scannable(c.oid) AS isscannable,                                                                                                                                                            +
+                                 |     c.relispopulated AS ispopulated,                                                                                                                                                                           +
                                  |     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..975f8dd57506e533ffd01364b57f17a830dd2304 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -15,10 +15,10 @@ SELECT * FROM tv ORDER BY type;
 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 relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
 SELECT * FROM tm;
 REFRESH MATERIALIZED VIEW tm;
-SELECT pg_relation_is_scannable('tm'::regclass);
+SELECT relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
 CREATE UNIQUE INDEX tm_type ON tm (type);
 SELECT * FROM tm;
 
@@ -109,7 +109,7 @@ CREATE VIEW v_test2 AS SELECT moo, 2*moo FROM v_test1 UNION ALL SELECT moo, 3*mo
 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);
+SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass;
 
 DROP VIEW v_test1 CASCADE;