From 1d6c72a55b23554cfb946527dc77f9d80044ae2c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 6 May 2013 13:26:51 -0400 Subject: [PATCH] Move materialized views' is-populated status into their pg_class entries. Previously this state was represented by whether the view's disk file had zero or nonzero size, which is problematic for numerous reasons, since it's breaking a fundamental assumption about heap storage. This was done to allow unlogged matviews to revert to unpopulated status after a crash despite our lack of any ability to update catalog entries post-crash. However, this poses enough risk of future problems that it seems better to not support unlogged matviews until we can find another way. Accordingly, revert that choice as well as a number of existing kluges forced by it in favor of creating a pg_class.relispopulated flag column. --- doc/src/sgml/catalogs.sgml | 14 ++++-- doc/src/sgml/func.sgml | 9 ---- src/backend/catalog/heap.c | 21 +-------- src/backend/catalog/system_views.sql | 2 +- src/backend/commands/cluster.c | 7 +-- src/backend/commands/createas.c | 11 +++-- src/backend/commands/matview.c | 68 ++++++++++++++++----------- src/backend/commands/vacuumlazy.c | 6 --- src/backend/utils/adt/dbsize.c | 27 ----------- src/backend/utils/cache/relcache.c | 34 ++++---------- src/bin/pg_dump/pg_dump.c | 54 +++++++++++++-------- src/bin/pg_dump/pg_dump.h | 2 +- src/include/catalog/catversion.h | 2 +- src/include/catalog/heap.h | 1 - src/include/catalog/pg_class.h | 20 ++++---- src/include/catalog/pg_proc.h | 2 - src/include/commands/matview.h | 2 +- src/include/utils/builtins.h | 1 - src/include/utils/rel.h | 11 ++++- src/test/regress/expected/matview.out | 18 +++---- src/test/regress/expected/rules.out | 2 +- src/test/regress/sql/matview.sql | 6 +-- 22 files changed, 141 insertions(+), 179 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 6c0ef5bd19..ec5232c91a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1863,6 +1863,14 @@ True if table has (or once had) any inheritance children + + relispopulated + bool + + True if relation is populated (this is true for all + relations other than some materialized views) + + relfrozenxid xid @@ -7776,14 +7784,14 @@ hasindexes boolean - pg_class.relhasindex + True if materialized view has (or recently had) any indexes - isscannable + ispopulated boolean - True if materialized view can currently be scanned + True if materialized view is currently populated definition diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 3ae2f23390..af00527fde 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); pg_tablespace_location - - pg_relation_is_scannable - - pg_typeof @@ -14410,11 +14406,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); text get the path in the file system that this tablespace is located in - - pg_relation_is_scannable(relation_oid) - boolean - is the relation scannable; a materialized view which has not been loaded will not be scannable - pg_typeof(any) regtype diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 0b4c659ff0..24a8474cb5 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 57adbf607d..a03bfa684b 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 ed62246cc5..878b6254f5 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 de65c4c781..14973f8e7c 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 da373045cc..5491c84c76 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 02f3cf3c20..9d304153b8 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 d32d9014c7..4c4e1ed820 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 670fa8c166..7888d38723 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 9ecfd0c6aa..76628fcbc3 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 7970a359bd..6352adaa05 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 42eb4de279..392649c37e 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 97d507e4c2..6b60d55a36 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 fd97141e9e..0f7ad5d743 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 ef892978ff..685b9c76cf 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 09bc384086..e3ce2f2953 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 e71876502e..15b60abfcd 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 632743af94..4b833c5018 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 06bb2551a8..a98de4f58d 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 a4ecfd2aea..dc3c8a8381 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 09a7378133..975f8dd575 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; -- 2.40.0