Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1d6c72a

Browse files
committed
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.
1 parent 5da5798 commit 1d6c72a

File tree

22 files changed

+141
-179
lines changed

22 files changed

+141
-179
lines changed

doc/src/sgml/catalogs.sgml

+11-3
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,14 @@
18631863
<entry>True if table has (or once had) any inheritance children</entry>
18641864
</row>
18651865

1866+
<row>
1867+
<entry><structfield>relispopulated</structfield></entry>
1868+
<entry><type>bool</type></entry>
1869+
<entry></entry>
1870+
<entry>True if relation is populated (this is true for all
1871+
relations other than some materialized views)</entry>
1872+
</row>
1873+
18661874
<row>
18671875
<entry><structfield>relfrozenxid</structfield></entry>
18681876
<entry><type>xid</type></entry>
@@ -7776,14 +7784,14 @@
77767784
<row>
77777785
<entry><structfield>hasindexes</structfield></entry>
77787786
<entry><type>boolean</type></entry>
7779-
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relhasindex</literal></entry>
7787+
<entry></entry>
77807788
<entry>True if materialized view has (or recently had) any indexes</entry>
77817789
</row>
77827790
<row>
7783-
<entry><structfield>isscannable</structfield></entry>
7791+
<entry><structfield>ispopulated</structfield></entry>
77847792
<entry><type>boolean</type></entry>
77857793
<entry></entry>
7786-
<entry>True if materialized view can currently be scanned</entry>
7794+
<entry>True if materialized view is currently populated</entry>
77877795
</row>
77887796
<row>
77897797
<entry><structfield>definition</structfield></entry>

doc/src/sgml/func.sgml

-9
Original file line numberDiff line numberDiff line change
@@ -14238,10 +14238,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
1423814238
<primary>pg_tablespace_location</primary>
1423914239
</indexterm>
1424014240

14241-
<indexterm>
14242-
<primary>pg_relation_is_scannable</primary>
14243-
</indexterm>
14244-
1424514241
<indexterm>
1424614242
<primary>pg_typeof</primary>
1424714243
</indexterm>
@@ -14410,11 +14406,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
1441014406
<entry><type>text</type></entry>
1441114407
<entry>get the path in the file system that this tablespace is located in</entry>
1441214408
</row>
14413-
<row>
14414-
<entry><literal><function>pg_relation_is_scannable(<parameter>relation_oid</parameter>)</function></literal></entry>
14415-
<entry><type>boolean</type></entry>
14416-
<entry>is the relation scannable; a materialized view which has not been loaded will not be scannable</entry>
14417-
</row>
1441814409
<row>
1441914410
<entry><literal><function>pg_typeof(<parameter>any</parameter>)</function></literal></entry>
1442014411
<entry><type>regtype</type></entry>

src/backend/catalog/heap.c

+1-20
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ InsertPgClassTuple(Relation pg_class_desc,
780780
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
781781
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
782782
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
783+
values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
783784
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
784785
values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid);
785786
if (relacl != (Datum) 0)
@@ -1345,26 +1346,6 @@ heap_create_init_fork(Relation rel)
13451346
smgrimmedsync(rel->rd_smgr, INIT_FORKNUM);
13461347
}
13471348

1348-
/*
1349-
* Check whether a materialized view is in an initial, unloaded state.
1350-
*
1351-
* The check here must match what is set up in heap_create_init_fork().
1352-
* Currently the init fork is an empty file. A missing heap is also
1353-
* considered to be unloaded.
1354-
*/
1355-
bool
1356-
heap_is_matview_init_state(Relation rel)
1357-
{
1358-
Assert(rel->rd_rel->relkind == RELKIND_MATVIEW);
1359-
1360-
RelationOpenSmgr(rel);
1361-
1362-
if (!smgrexists(rel->rd_smgr, MAIN_FORKNUM))
1363-
return true;
1364-
1365-
return (smgrnblocks(rel->rd_smgr, MAIN_FORKNUM) < 1);
1366-
}
1367-
13681349
/*
13691350
* RelationRemoveInheritance
13701351
*

src/backend/catalog/system_views.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ CREATE VIEW pg_matviews AS
101101
pg_get_userbyid(C.relowner) AS matviewowner,
102102
T.spcname AS tablespace,
103103
C.relhasindex AS hasindexes,
104-
pg_relation_is_scannable(C.oid) AS isscannable,
104+
C.relispopulated AS ispopulated,
105105
pg_get_viewdef(C.oid) AS definition
106106
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
107107
LEFT JOIN pg_tablespace T ON (T.oid = C.reltablespace)

src/backend/commands/cluster.c

+1-6
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
#include "catalog/objectaccess.h"
3131
#include "catalog/toasting.h"
3232
#include "commands/cluster.h"
33-
#include "commands/matview.h"
3433
#include "commands/tablecmds.h"
3534
#include "commands/vacuum.h"
3635
#include "miscadmin.h"
@@ -388,7 +387,7 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
388387
* database.
389388
*/
390389
if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
391-
!OldHeap->rd_ispopulated)
390+
!RelationIsPopulated(OldHeap))
392391
{
393392
relation_close(OldHeap, AccessExclusiveLock);
394393
return;
@@ -922,10 +921,6 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
922921
get_namespace_name(RelationGetNamespace(OldHeap)),
923922
RelationGetRelationName(OldHeap))));
924923

925-
if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW)
926-
/* Make sure the heap looks good even if no rows are written. */
927-
SetMatViewToPopulated(NewHeap);
928-
929924
/*
930925
* Scan through the OldHeap, either in OldIndex order or sequentially;
931926
* copy each tuple into the NewHeap, or transiently to the tuplesort

src/backend/commands/createas.c

+7-4
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,6 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
359359
*/
360360
intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
361361

362-
if (is_matview && !into->skipData)
363-
/* Make sure the heap looks good even if no rows are written. */
364-
SetMatViewToPopulated(intoRelationDesc);
365-
366362
/*
367363
* Check INSERT permission on the constructed table.
368364
*
@@ -381,6 +377,13 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
381377

382378
ExecCheckRTPerms(list_make1(rte), true);
383379

380+
/*
381+
* Tentatively mark the target as populated, if it's a matview and we're
382+
* going to fill it; otherwise, no change needed.
383+
*/
384+
if (is_matview && !into->skipData)
385+
SetMatViewPopulatedState(intoRelationDesc, true);
386+
384387
/*
385388
* Fill private fields of myState for use by later routines
386389
*/

src/backend/commands/matview.c

+40-28
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,23 @@
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

3636
typedef 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
*/
6360
void
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
*/
109116
void
110117
ExecRefreshMatView(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
}

src/backend/commands/vacuumlazy.c

-6
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
230230
*
231231
* Don't even think about it unless we have a shot at releasing a goodly
232232
* number of pages. Otherwise, the time taken isn't worth it.
233-
*
234-
* Leave a populated materialized view with at least one page.
235233
*/
236-
if (onerel->rd_rel->relkind == RELKIND_MATVIEW &&
237-
vacrelstats->nonempty_pages == 0)
238-
vacrelstats->nonempty_pages = 1;
239-
240234
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
241235
if (possibly_freeable > 0 &&
242236
(possibly_freeable >= REL_TRUNCATE_MINIMUM ||

src/backend/utils/adt/dbsize.c

-27
Original file line numberDiff line numberDiff line change
@@ -834,30 +834,3 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
834834

835835
PG_RETURN_TEXT_P(cstring_to_text(path));
836836
}
837-
838-
839-
/*
840-
* Indicate whether a relation is scannable.
841-
*
842-
* Currently, this is always true except for a materialized view which has not
843-
* been populated. It is expected that other conditions for allowing a
844-
* materialized view to be scanned will be added in later releases.
845-
*/
846-
Datum
847-
pg_relation_is_scannable(PG_FUNCTION_ARGS)
848-
{
849-
Oid relid;
850-
Relation relation;
851-
bool result;
852-
853-
relid = PG_GETARG_OID(0);
854-
relation = try_relation_open(relid, AccessShareLock);
855-
856-
if (relation == NULL)
857-
PG_RETURN_BOOL(false);
858-
859-
result = RelationIsScannable(relation);
860-
861-
relation_close(relation, AccessShareLock);
862-
PG_RETURN_BOOL(result);
863-
}

0 commit comments

Comments
 (0)