diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2013-05-06 13:26:51 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2013-05-06 13:27:22 -0400 |
commit | 1d6c72a55b23554cfb946527dc77f9d80044ae2c (patch) | |
tree | 8f7964a0114022585c1a0d90f37d2fd466bb6154 /src/backend/commands/matview.c | |
parent | 5da5798004e90b14332918e7db702271442d465d (diff) | |
download | postgresql-1d6c72a55b23554cfb946527dc77f9d80044ae2c.tar.gz postgresql-1d6c72a55b23554cfb946527dc77f9d80044ae2c.zip |
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.
Diffstat (limited to 'src/backend/commands/matview.c')
-rw-r--r-- | src/backend/commands/matview.c | 68 |
1 files changed, 40 insertions, 28 deletions
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index da373045cc0..5491c84c766 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); } |