mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
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.
This commit is contained in:
@ -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);
|
||||
/*
|
||||
* 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));
|
||||
|
||||
if (RelationNeedsWAL(relation))
|
||||
log_newpage(&(relation->rd_node), MAIN_FORKNUM, 0, page);
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relispopulated = newstate;
|
||||
|
||||
RelationOpenSmgr(relation);
|
||||
simple_heap_update(pgrel, &tuple->t_self, tuple);
|
||||
|
||||
PageSetChecksumInplace(page, 0);
|
||||
smgrextend(relation->rd_smgr, MAIN_FORKNUM, 0, (char *) page, true);
|
||||
CatalogUpdateIndexes(pgrel, tuple);
|
||||
|
||||
pfree(page);
|
||||
heap_freetuple(tuple);
|
||||
heap_close(pgrel, RowExclusiveLock);
|
||||
|
||||
smgrimmedsync(relation->rd_smgr, MAIN_FORKNUM);
|
||||
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user