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