1414 */
1515#include "postgres.h"
1616
17- #include "access/heapam_xlog .h"
17+ #include "access/htup_details .h"
1818#include "access/multixact.h"
19- #include "access/relscan.h"
2019#include "access/xact.h"
2120#include "catalog/catalog.h"
22- #include "catalog/heap .h"
21+ #include "catalog/indexing .h"
2322#include "catalog/namespace.h"
2423#include "commands/cluster.h"
2524#include "commands/matview.h"
2625#include "commands/tablecmds.h"
2726#include "executor/executor.h"
2827#include "miscadmin.h"
2928#include "rewrite/rewriteHandler.h"
30- #include "storage/lmgr.h"
3129#include "storage/smgr.h"
3230#include "tcop/tcopprot.h"
31+ #include "utils/rel.h"
3332#include "utils/snapmgr.h"
33+ #include "utils/syscache.h"
3434
3535
3636typedef struct
@@ -52,38 +52,45 @@ static void refresh_matview_datafill(DestReceiver *dest, Query *query,
5252 const char * queryString );
5353
5454/*
55- * SetMatViewToPopulated
56- * Indicate that the materialized view has been populated by its query.
57- *
58- * NOTE: The heap starts out in a state that doesn't look scannable, and can
59- * only transition from there to scannable at the time a new heap is created.
55+ * SetMatViewPopulatedState
56+ * Mark a materialized view as populated, or not.
6057 *
6158 * NOTE: caller must be holding an appropriate lock on the relation.
6259 */
6360void
64- SetMatViewToPopulated (Relation relation )
61+ SetMatViewPopulatedState (Relation relation , bool newstate )
6562{
66- Page page ;
63+ Relation pgrel ;
64+ HeapTuple tuple ;
6765
6866 Assert (relation -> rd_rel -> relkind == RELKIND_MATVIEW );
69- Assert (relation -> rd_ispopulated == false);
70-
71- page = (Page ) palloc (BLCKSZ );
72- PageInit (page , BLCKSZ , 0 );
7367
74- if (RelationNeedsWAL (relation ))
75- log_newpage (& (relation -> rd_node ), MAIN_FORKNUM , 0 , page );
68+ /*
69+ * Update relation's pg_class entry. Crucial side-effect: other backends
70+ * (and this one too!) are sent SI message to make them rebuild relcache
71+ * entries.
72+ */
73+ pgrel = heap_open (RelationRelationId , RowExclusiveLock );
74+ tuple = SearchSysCacheCopy1 (RELOID ,
75+ ObjectIdGetDatum (RelationGetRelid (relation )));
76+ if (!HeapTupleIsValid (tuple ))
77+ elog (ERROR , "cache lookup failed for relation %u" ,
78+ RelationGetRelid (relation ));
7679
77- RelationOpenSmgr ( relation ) ;
80+ (( Form_pg_class ) GETSTRUCT ( tuple )) -> relispopulated = newstate ;
7881
79- PageSetChecksumInplace (page , 0 );
80- smgrextend (relation -> rd_smgr , MAIN_FORKNUM , 0 , (char * ) page , true);
82+ simple_heap_update (pgrel , & tuple -> t_self , tuple );
8183
82- pfree ( page );
84+ CatalogUpdateIndexes ( pgrel , tuple );
8385
84- smgrimmedsync (relation -> rd_smgr , MAIN_FORKNUM );
86+ heap_freetuple (tuple );
87+ heap_close (pgrel , RowExclusiveLock );
8588
86- RelationCacheInvalidateEntry (relation -> rd_id );
89+ /*
90+ * Advance command counter to make the updated pg_class row locally
91+ * visible.
92+ */
93+ CommandCounterIncrement ();
8794}
8895
8996/*
@@ -97,14 +104,14 @@ SetMatViewToPopulated(Relation relation)
97104 * If WITH NO DATA was specified, this is effectively like a TRUNCATE;
98105 * otherwise it is like a TRUNCATE followed by an INSERT using the SELECT
99106 * statement associated with the materialized view. The statement node's
100- * skipData field is used to indicate that the clause was used.
107+ * skipData field shows whether the clause was used.
101108 *
102109 * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
103110 * the new heap, it's better to create the indexes afterwards than to fill them
104111 * incrementally while we load.
105112 *
106- * The scannable state is changed based on whether the contents reflect the
107- * result set of the materialized view's query.
113+ * The matview's "populated" state is changed based on whether the contents
114+ * reflect the result set of the materialized view's query.
108115 */
109116void
110117ExecRefreshMatView (RefreshMatViewStmt * stmt , const char * queryString ,
@@ -184,6 +191,12 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
184191 */
185192 CheckTableNotInUse (matviewRel , "REFRESH MATERIALIZED VIEW" );
186193
194+ /*
195+ * Tentatively mark the matview as populated or not (this will roll back
196+ * if we fail later).
197+ */
198+ SetMatViewPopulatedState (matviewRel , !stmt -> skipData );
199+
187200 tableSpace = matviewRel -> rd_rel -> reltablespace ;
188201
189202 heap_close (matviewRel , NoLock );
@@ -192,6 +205,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
192205 OIDNewHeap = make_new_heap (matviewOid , tableSpace );
193206 dest = CreateTransientRelDestReceiver (OIDNewHeap );
194207
208+ /* Generate the data, if wanted. */
195209 if (!stmt -> skipData )
196210 refresh_matview_datafill (dest , dataQuery , queryString );
197211
@@ -300,8 +314,6 @@ transientrel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
300314 myState -> hi_options |= HEAP_INSERT_SKIP_WAL ;
301315 myState -> bistate = GetBulkInsertState ();
302316
303- SetMatViewToPopulated (transientrel );
304-
305317 /* Not using WAL requires smgr_targblock be initially invalid */
306318 Assert (RelationGetTargetBlock (transientrel ) == InvalidBlockNumber );
307319}
0 commit comments