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

Lines changed: 11 additions & 3 deletions
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

Lines changed: 0 additions & 9 deletions
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

Lines changed: 1 addition & 20 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 6 deletions
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

Lines changed: 7 additions & 4 deletions
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

Lines changed: 40 additions & 28 deletions
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

Lines changed: 0 additions & 6 deletions
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

Lines changed: 0 additions & 27 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy