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:
parent
5da5798004
commit
1d6c72a55b
@ -1863,6 +1863,14 @@
|
||||
<entry>True if table has (or once had) any inheritance children</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relispopulated</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
<entry></entry>
|
||||
<entry>True if relation is populated (this is true for all
|
||||
relations other than some materialized views)</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relfrozenxid</structfield></entry>
|
||||
<entry><type>xid</type></entry>
|
||||
@ -7776,14 +7784,14 @@
|
||||
<row>
|
||||
<entry><structfield>hasindexes</structfield></entry>
|
||||
<entry><type>boolean</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relhasindex</literal></entry>
|
||||
<entry></entry>
|
||||
<entry>True if materialized view has (or recently had) any indexes</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><structfield>isscannable</structfield></entry>
|
||||
<entry><structfield>ispopulated</structfield></entry>
|
||||
<entry><type>boolean</type></entry>
|
||||
<entry></entry>
|
||||
<entry>True if materialized view can currently be scanned</entry>
|
||||
<entry>True if materialized view is currently populated</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><structfield>definition</structfield></entry>
|
||||
|
@ -14238,10 +14238,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
||||
<primary>pg_tablespace_location</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_relation_is_scannable</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_typeof</primary>
|
||||
</indexterm>
|
||||
@ -14410,11 +14406,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
||||
<entry><type>text</type></entry>
|
||||
<entry>get the path in the file system that this tablespace is located in</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal><function>pg_relation_is_scannable(<parameter>relation_oid</parameter>)</function></literal></entry>
|
||||
<entry><type>boolean</type></entry>
|
||||
<entry>is the relation scannable; a materialized view which has not been loaded will not be scannable</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal><function>pg_typeof(<parameter>any</parameter>)</function></literal></entry>
|
||||
<entry><type>regtype</type></entry>
|
||||
|
@ -780,6 +780,7 @@ InsertPgClassTuple(Relation pg_class_desc,
|
||||
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
|
||||
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
|
||||
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
|
||||
values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
|
||||
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
|
||||
values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid);
|
||||
if (relacl != (Datum) 0)
|
||||
@ -1345,26 +1346,6 @@ heap_create_init_fork(Relation rel)
|
||||
smgrimmedsync(rel->rd_smgr, INIT_FORKNUM);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a materialized view is in an initial, unloaded state.
|
||||
*
|
||||
* The check here must match what is set up in heap_create_init_fork().
|
||||
* Currently the init fork is an empty file. A missing heap is also
|
||||
* considered to be unloaded.
|
||||
*/
|
||||
bool
|
||||
heap_is_matview_init_state(Relation rel)
|
||||
{
|
||||
Assert(rel->rd_rel->relkind == RELKIND_MATVIEW);
|
||||
|
||||
RelationOpenSmgr(rel);
|
||||
|
||||
if (!smgrexists(rel->rd_smgr, MAIN_FORKNUM))
|
||||
return true;
|
||||
|
||||
return (smgrnblocks(rel->rd_smgr, MAIN_FORKNUM) < 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* RelationRemoveInheritance
|
||||
*
|
||||
|
@ -101,7 +101,7 @@ CREATE VIEW pg_matviews AS
|
||||
pg_get_userbyid(C.relowner) AS matviewowner,
|
||||
T.spcname AS tablespace,
|
||||
C.relhasindex AS hasindexes,
|
||||
pg_relation_is_scannable(C.oid) AS isscannable,
|
||||
C.relispopulated AS ispopulated,
|
||||
pg_get_viewdef(C.oid) AS definition
|
||||
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||
LEFT JOIN pg_tablespace T ON (T.oid = C.reltablespace)
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include "catalog/objectaccess.h"
|
||||
#include "catalog/toasting.h"
|
||||
#include "commands/cluster.h"
|
||||
#include "commands/matview.h"
|
||||
#include "commands/tablecmds.h"
|
||||
#include "commands/vacuum.h"
|
||||
#include "miscadmin.h"
|
||||
@ -388,7 +387,7 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
|
||||
* database.
|
||||
*/
|
||||
if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
|
||||
!OldHeap->rd_ispopulated)
|
||||
!RelationIsPopulated(OldHeap))
|
||||
{
|
||||
relation_close(OldHeap, AccessExclusiveLock);
|
||||
return;
|
||||
@ -922,10 +921,6 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
|
||||
get_namespace_name(RelationGetNamespace(OldHeap)),
|
||||
RelationGetRelationName(OldHeap))));
|
||||
|
||||
if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW)
|
||||
/* Make sure the heap looks good even if no rows are written. */
|
||||
SetMatViewToPopulated(NewHeap);
|
||||
|
||||
/*
|
||||
* Scan through the OldHeap, either in OldIndex order or sequentially;
|
||||
* copy each tuple into the NewHeap, or transiently to the tuplesort
|
||||
|
@ -359,10 +359,6 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||
*/
|
||||
intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
|
||||
|
||||
if (is_matview && !into->skipData)
|
||||
/* Make sure the heap looks good even if no rows are written. */
|
||||
SetMatViewToPopulated(intoRelationDesc);
|
||||
|
||||
/*
|
||||
* Check INSERT permission on the constructed table.
|
||||
*
|
||||
@ -381,6 +377,13 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||
|
||||
ExecCheckRTPerms(list_make1(rte), true);
|
||||
|
||||
/*
|
||||
* Tentatively mark the target as populated, if it's a matview and we're
|
||||
* going to fill it; otherwise, no change needed.
|
||||
*/
|
||||
if (is_matview && !into->skipData)
|
||||
SetMatViewPopulatedState(intoRelationDesc, true);
|
||||
|
||||
/*
|
||||
* Fill private fields of myState for use by later routines
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -230,13 +230,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
|
||||
*
|
||||
* Don't even think about it unless we have a shot at releasing a goodly
|
||||
* number of pages. Otherwise, the time taken isn't worth it.
|
||||
*
|
||||
* Leave a populated materialized view with at least one page.
|
||||
*/
|
||||
if (onerel->rd_rel->relkind == RELKIND_MATVIEW &&
|
||||
vacrelstats->nonempty_pages == 0)
|
||||
vacrelstats->nonempty_pages = 1;
|
||||
|
||||
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
|
||||
if (possibly_freeable > 0 &&
|
||||
(possibly_freeable >= REL_TRUNCATE_MINIMUM ||
|
||||
|
@ -834,30 +834,3 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
|
||||
|
||||
PG_RETURN_TEXT_P(cstring_to_text(path));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Indicate whether a relation is scannable.
|
||||
*
|
||||
* Currently, this is always true except for a materialized view which has not
|
||||
* been populated. It is expected that other conditions for allowing a
|
||||
* materialized view to be scanned will be added in later releases.
|
||||
*/
|
||||
Datum
|
||||
pg_relation_is_scannable(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid relid;
|
||||
Relation relation;
|
||||
bool result;
|
||||
|
||||
relid = PG_GETARG_OID(0);
|
||||
relation = try_relation_open(relid, AccessShareLock);
|
||||
|
||||
if (relation == NULL)
|
||||
PG_RETURN_BOOL(false);
|
||||
|
||||
result = RelationIsScannable(relation);
|
||||
|
||||
relation_close(relation, AccessShareLock);
|
||||
PG_RETURN_BOOL(result);
|
||||
}
|
||||
|
34
src/backend/utils/cache/relcache.c
vendored
34
src/backend/utils/cache/relcache.c
vendored
@ -37,7 +37,6 @@
|
||||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
@ -956,12 +955,6 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
|
||||
/* make sure relation is marked as having no open file yet */
|
||||
relation->rd_smgr = NULL;
|
||||
|
||||
if (relation->rd_rel->relkind == RELKIND_MATVIEW &&
|
||||
heap_is_matview_init_state(relation))
|
||||
relation->rd_ispopulated = false;
|
||||
else
|
||||
relation->rd_ispopulated = true;
|
||||
|
||||
/*
|
||||
* now we can free the memory allocated for pg_class_tuple
|
||||
*/
|
||||
@ -1459,6 +1452,9 @@ formrdesc(const char *relationName, Oid relationReltype,
|
||||
/* formrdesc is used only for permanent relations */
|
||||
relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
|
||||
|
||||
/* ... and they're always populated, too */
|
||||
relation->rd_rel->relispopulated = true;
|
||||
|
||||
relation->rd_rel->relpages = 0;
|
||||
relation->rd_rel->reltuples = 0;
|
||||
relation->rd_rel->relallvisible = 0;
|
||||
@ -1531,7 +1527,6 @@ formrdesc(const char *relationName, Oid relationReltype,
|
||||
* initialize physical addressing information for the relation
|
||||
*/
|
||||
RelationInitPhysicalAddr(relation);
|
||||
relation->rd_ispopulated = true;
|
||||
|
||||
/*
|
||||
* initialize the rel-has-index flag, using hardwired knowledge
|
||||
@ -1756,7 +1751,6 @@ RelationReloadIndexInfo(Relation relation)
|
||||
heap_freetuple(pg_class_tuple);
|
||||
/* We must recalculate physical address in case it changed */
|
||||
RelationInitPhysicalAddr(relation);
|
||||
relation->rd_ispopulated = true;
|
||||
|
||||
/*
|
||||
* For a non-system index, there are fields of the pg_index row that are
|
||||
@ -1905,11 +1899,6 @@ RelationClearRelation(Relation relation, bool rebuild)
|
||||
if (relation->rd_isnailed)
|
||||
{
|
||||
RelationInitPhysicalAddr(relation);
|
||||
if (relation->rd_rel->relkind == RELKIND_MATVIEW &&
|
||||
heap_is_matview_init_state(relation))
|
||||
relation->rd_ispopulated = false;
|
||||
else
|
||||
relation->rd_ispopulated = true;
|
||||
|
||||
if (relation->rd_rel->relkind == RELKIND_INDEX)
|
||||
{
|
||||
@ -2671,6 +2660,12 @@ RelationBuildLocalRelation(const char *relname,
|
||||
break;
|
||||
}
|
||||
|
||||
/* if it's a materialized view, it's not populated initially */
|
||||
if (relkind == RELKIND_MATVIEW)
|
||||
rel->rd_rel->relispopulated = false;
|
||||
else
|
||||
rel->rd_rel->relispopulated = true;
|
||||
|
||||
/*
|
||||
* Insert relation physical and logical identifiers (OIDs) into the right
|
||||
* places. For a mapped relation, we set relfilenode to zero and rely on
|
||||
@ -2698,12 +2693,6 @@ RelationBuildLocalRelation(const char *relname,
|
||||
|
||||
RelationInitPhysicalAddr(rel);
|
||||
|
||||
/* materialized view not initially scannable */
|
||||
if (relkind == RELKIND_MATVIEW)
|
||||
rel->rd_ispopulated = false;
|
||||
else
|
||||
rel->rd_ispopulated = true;
|
||||
|
||||
/*
|
||||
* Okay to insert into the relcache hash tables.
|
||||
*/
|
||||
@ -4448,11 +4437,6 @@ load_relcache_init_file(bool shared)
|
||||
*/
|
||||
RelationInitLockInfo(rel);
|
||||
RelationInitPhysicalAddr(rel);
|
||||
if (rel->rd_rel->relkind == RELKIND_MATVIEW &&
|
||||
heap_is_matview_init_state(rel))
|
||||
rel->rd_ispopulated = false;
|
||||
else
|
||||
rel->rd_ispopulated = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1759,8 +1759,8 @@ refreshMatViewData(Archive *fout, TableDataInfo *tdinfo)
|
||||
TableInfo *tbinfo = tdinfo->tdtable;
|
||||
PQExpBuffer q;
|
||||
|
||||
/* If the materialized view is not flagged as scannable, skip this. */
|
||||
if (!tbinfo->isscannable)
|
||||
/* If the materialized view is not flagged as populated, skip this. */
|
||||
if (!tbinfo->relispopulated)
|
||||
return;
|
||||
|
||||
q = createPQExpBuffer();
|
||||
@ -1967,8 +1967,8 @@ buildMatViewRefreshDependencies(Archive *fout)
|
||||
|
||||
addObjectDependency(dobj, refdobj->dumpId);
|
||||
|
||||
if (!reftbinfo->isscannable)
|
||||
tbinfo->isscannable = false;
|
||||
if (!reftbinfo->relispopulated)
|
||||
tbinfo->relispopulated = false;
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
@ -4219,7 +4219,7 @@ getTables(Archive *fout, int *numTables)
|
||||
int i_toastoid;
|
||||
int i_toastfrozenxid;
|
||||
int i_relpersistence;
|
||||
int i_isscannable;
|
||||
int i_relispopulated;
|
||||
int i_owning_tab;
|
||||
int i_owning_col;
|
||||
int i_reltablespace;
|
||||
@ -4265,8 +4265,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"c.relfrozenxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"c.relpersistence, "
|
||||
"CASE WHEN c.relkind = '%c' THEN pg_relation_is_scannable(c.oid) ELSE 't'::bool END as isscannable, "
|
||||
"c.relpersistence, c.relispopulated, "
|
||||
"c.relpages, "
|
||||
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
|
||||
"d.refobjid AS owning_tab, "
|
||||
@ -4284,7 +4283,6 @@ getTables(Archive *fout, int *numTables)
|
||||
"WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
|
||||
"ORDER BY c.oid",
|
||||
username_subquery,
|
||||
RELKIND_MATVIEW,
|
||||
RELKIND_SEQUENCE,
|
||||
RELKIND_RELATION, RELKIND_SEQUENCE,
|
||||
RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
|
||||
@ -4304,7 +4302,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"c.relfrozenxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"c.relpersistence, 't'::bool as isscannable, "
|
||||
"c.relpersistence, 't' as relispopulated, "
|
||||
"c.relpages, "
|
||||
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
|
||||
"d.refobjid AS owning_tab, "
|
||||
@ -4341,7 +4339,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"c.relfrozenxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"c.relpages, "
|
||||
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
|
||||
"d.refobjid AS owning_tab, "
|
||||
@ -4377,7 +4375,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"c.relfrozenxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"c.relpages, "
|
||||
"NULL AS reloftype, "
|
||||
"d.refobjid AS owning_tab, "
|
||||
@ -4413,7 +4411,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"c.relfrozenxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"c.relpages, "
|
||||
"NULL AS reloftype, "
|
||||
"d.refobjid AS owning_tab, "
|
||||
@ -4450,7 +4448,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"0 AS relfrozenxid, "
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"relpages, "
|
||||
"NULL AS reloftype, "
|
||||
"d.refobjid AS owning_tab, "
|
||||
@ -4486,7 +4484,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"0 AS relfrozenxid, "
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"relpages, "
|
||||
"NULL AS reloftype, "
|
||||
"d.refobjid AS owning_tab, "
|
||||
@ -4518,7 +4516,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"0 AS relfrozenxid, "
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"relpages, "
|
||||
"NULL AS reloftype, "
|
||||
"NULL::oid AS owning_tab, "
|
||||
@ -4545,7 +4543,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"0 AS relfrozenxid, "
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"relpages, "
|
||||
"NULL AS reloftype, "
|
||||
"NULL::oid AS owning_tab, "
|
||||
@ -4582,7 +4580,7 @@ getTables(Archive *fout, int *numTables)
|
||||
"0 as relfrozenxid, "
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, "
|
||||
"'p' AS relpersistence, 't'::bool as isscannable, "
|
||||
"'p' AS relpersistence, 't' as relispopulated, "
|
||||
"0 AS relpages, "
|
||||
"NULL AS reloftype, "
|
||||
"NULL::oid AS owning_tab, "
|
||||
@ -4631,7 +4629,7 @@ getTables(Archive *fout, int *numTables)
|
||||
i_toastoid = PQfnumber(res, "toid");
|
||||
i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
|
||||
i_relpersistence = PQfnumber(res, "relpersistence");
|
||||
i_isscannable = PQfnumber(res, "isscannable");
|
||||
i_relispopulated = PQfnumber(res, "relispopulated");
|
||||
i_relpages = PQfnumber(res, "relpages");
|
||||
i_owning_tab = PQfnumber(res, "owning_tab");
|
||||
i_owning_col = PQfnumber(res, "owning_col");
|
||||
@ -4674,7 +4672,7 @@ getTables(Archive *fout, int *numTables)
|
||||
tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
|
||||
tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
|
||||
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
|
||||
tblinfo[i].isscannable = (strcmp(PQgetvalue(res, i, i_isscannable), "t") == 0);
|
||||
tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
|
||||
tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
|
||||
tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
|
||||
tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
|
||||
@ -13101,6 +13099,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
|
||||
|
||||
/*
|
||||
* For materialized views, create the AS clause just like a view.
|
||||
* At this point, we always mark the view as not populated.
|
||||
*/
|
||||
if (tbinfo->relkind == RELKIND_MATVIEW)
|
||||
{
|
||||
@ -13229,6 +13228,23 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In binary_upgrade mode, restore matviews' populated status by
|
||||
* poking pg_class directly. This is pretty ugly, but we can't use
|
||||
* REFRESH MATERIALIZED VIEW since it's possible that some underlying
|
||||
* matview is not populated even though this matview is.
|
||||
*/
|
||||
if (binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
|
||||
tbinfo->relispopulated)
|
||||
{
|
||||
appendPQExpBuffer(q, "\n-- For binary upgrade, mark materialized view as populated\n");
|
||||
appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
|
||||
"SET relispopulated = 't'\n"
|
||||
"WHERE oid = ");
|
||||
appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout);
|
||||
appendPQExpBuffer(q, "::pg_catalog.regclass;\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump additional per-column properties that we can't handle in the
|
||||
* main CREATE TABLE command.
|
||||
|
@ -236,6 +236,7 @@ typedef struct _tableInfo
|
||||
char *relacl;
|
||||
char relkind;
|
||||
char relpersistence; /* relation persistence */
|
||||
bool relispopulated; /* relation is populated */
|
||||
char *reltablespace; /* relation tablespace */
|
||||
char *reloptions; /* options specified by WITH (...) */
|
||||
char *toast_reloptions; /* ditto, for the TOAST table */
|
||||
@ -243,7 +244,6 @@ typedef struct _tableInfo
|
||||
bool hasrules; /* does it have any rules? */
|
||||
bool hastriggers; /* does it have any triggers? */
|
||||
bool hasoids; /* does it have OIDs? */
|
||||
bool isscannable; /* is valid for use in queries */
|
||||
uint32 frozenxid; /* for restore frozen xid */
|
||||
Oid toast_oid; /* for restore toast frozen xid */
|
||||
uint32 toast_frozenxid; /* for restore toast frozen xid */
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201304271
|
||||
#define CATALOG_VERSION_NO 201305061
|
||||
|
||||
#endif
|
||||
|
@ -70,7 +70,6 @@ extern Oid heap_create_with_catalog(const char *relname,
|
||||
bool is_internal);
|
||||
|
||||
extern void heap_create_init_fork(Relation rel);
|
||||
extern bool heap_is_matview_init_state(Relation rel);
|
||||
|
||||
extern void heap_drop_with_catalog(Oid relid);
|
||||
|
||||
|
@ -66,6 +66,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
|
||||
bool relhasrules; /* has (or has had) any rules */
|
||||
bool relhastriggers; /* has (or has had) any TRIGGERs */
|
||||
bool relhassubclass; /* has (or has had) derived classes */
|
||||
bool relispopulated; /* matview currently holds query results */
|
||||
TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
|
||||
TransactionId relminmxid; /* all multixacts in this rel are >= this.
|
||||
* this is really a MultiXactId */
|
||||
@ -93,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class;
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
#define Natts_pg_class 28
|
||||
#define Natts_pg_class 29
|
||||
#define Anum_pg_class_relname 1
|
||||
#define Anum_pg_class_relnamespace 2
|
||||
#define Anum_pg_class_reltype 3
|
||||
@ -118,10 +119,11 @@ typedef FormData_pg_class *Form_pg_class;
|
||||
#define Anum_pg_class_relhasrules 22
|
||||
#define Anum_pg_class_relhastriggers 23
|
||||
#define Anum_pg_class_relhassubclass 24
|
||||
#define Anum_pg_class_relfrozenxid 25
|
||||
#define Anum_pg_class_relminmxid 26
|
||||
#define Anum_pg_class_relacl 27
|
||||
#define Anum_pg_class_reloptions 28
|
||||
#define Anum_pg_class_relispopulated 25
|
||||
#define Anum_pg_class_relfrozenxid 26
|
||||
#define Anum_pg_class_relminmxid 27
|
||||
#define Anum_pg_class_relacl 28
|
||||
#define Anum_pg_class_reloptions 29
|
||||
|
||||
/* ----------------
|
||||
* initial contents of pg_class
|
||||
@ -136,13 +138,13 @@ typedef FormData_pg_class *Form_pg_class;
|
||||
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
|
||||
* similarly, "1" in relminmxid stands for FirstMultiXactId
|
||||
*/
|
||||
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f 3 1 _null_ _null_ ));
|
||||
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f t 3 1 _null_ _null_ ));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f 3 1 _null_ _null_ ));
|
||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f t 3 1 _null_ _null_ ));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f 3 1 _null_ _null_ ));
|
||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f t 3 1 _null_ _null_ ));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 28 0 t f f f f 3 1 _null_ _null_ ));
|
||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 29 0 t f f f f t 3 1 _null_ _null_ ));
|
||||
DESCR("");
|
||||
|
||||
|
||||
|
@ -1980,8 +1980,6 @@ DATA(insert OID = 3842 ( pg_view_is_insertable PGNSP PGUID 12 10 0 0 0 f f f f
|
||||
DESCR("is a view insertable-into");
|
||||
DATA(insert OID = 3843 ( pg_view_is_updatable PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_view_is_updatable _null_ _null_ _null_ ));
|
||||
DESCR("is a view updatable");
|
||||
DATA(insert OID = 3846 ( pg_relation_is_scannable PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_relation_is_scannable _null_ _null_ _null_ ));
|
||||
DESCR("is a relation scannable");
|
||||
|
||||
/* Deferrable unique constraint trigger */
|
||||
DATA(insert OID = 1250 ( unique_key_recheck PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 2279 "" _null_ _null_ _null_ _null_ unique_key_recheck _null_ _null_ _null_ ));
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "utils/relcache.h"
|
||||
|
||||
|
||||
extern void SetMatViewToPopulated(Relation relation);
|
||||
extern void SetMatViewPopulatedState(Relation relation, bool newstate);
|
||||
|
||||
extern void ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
|
||||
ParamListInfo params, char *completionTag);
|
||||
|
@ -461,7 +461,6 @@ extern Datum pg_table_size(PG_FUNCTION_ARGS);
|
||||
extern Datum pg_indexes_size(PG_FUNCTION_ARGS);
|
||||
extern Datum pg_relation_filenode(PG_FUNCTION_ARGS);
|
||||
extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
|
||||
extern Datum pg_relation_is_scannable(PG_FUNCTION_ARGS);
|
||||
|
||||
/* genfile.c */
|
||||
extern bytea *read_binary_file(const char *filename,
|
||||
|
@ -77,7 +77,6 @@ typedef struct RelationData
|
||||
BackendId rd_backend; /* owning backend id, if temporary relation */
|
||||
bool rd_islocaltemp; /* rel is a temp rel of this session */
|
||||
bool rd_isnailed; /* rel is nailed in cache */
|
||||
bool rd_ispopulated; /* matview has query results */
|
||||
bool rd_isvalid; /* relcache entry is valid */
|
||||
char rd_indexvalid; /* state of rd_indexlist: 0 = not valid, 1 =
|
||||
* valid, 2 = temporarily forced */
|
||||
@ -408,7 +407,15 @@ typedef struct StdRdOptions
|
||||
* populated by its query. This is likely to get more complicated later,
|
||||
* so use a macro which looks like a function.
|
||||
*/
|
||||
#define RelationIsScannable(relation) ((relation)->rd_ispopulated)
|
||||
#define RelationIsScannable(relation) ((relation)->rd_rel->relispopulated)
|
||||
|
||||
/*
|
||||
* RelationIsPopulated
|
||||
* Currently, we don't physically distinguish the "populated" and
|
||||
* "scannable" properties of matviews, but that may change later.
|
||||
* Hence, use the appropriate one of these macros in code tests.
|
||||
*/
|
||||
#define RelationIsPopulated(relation) ((relation)->rd_rel->relispopulated)
|
||||
|
||||
|
||||
/* routines in utils/cache/relcache.c */
|
||||
|
@ -26,9 +26,9 @@ EXPLAIN (costs off)
|
||||
(2 rows)
|
||||
|
||||
CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
|
||||
SELECT pg_relation_is_scannable('tm'::regclass);
|
||||
pg_relation_is_scannable
|
||||
--------------------------
|
||||
SELECT relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
|
||||
relispopulated
|
||||
----------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
@ -36,9 +36,9 @@ SELECT * FROM tm;
|
||||
ERROR: materialized view "tm" has not been populated
|
||||
HINT: Use the REFRESH MATERIALIZED VIEW command.
|
||||
REFRESH MATERIALIZED VIEW tm;
|
||||
SELECT pg_relation_is_scannable('tm'::regclass);
|
||||
pg_relation_is_scannable
|
||||
--------------------------
|
||||
SELECT relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
|
||||
relispopulated
|
||||
----------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
@ -354,9 +354,9 @@ UNION ALL
|
||||
FROM v_test2;
|
||||
|
||||
CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345;
|
||||
SELECT pg_relation_is_scannable('mv_test3'::regclass);
|
||||
pg_relation_is_scannable
|
||||
--------------------------
|
||||
SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass;
|
||||
relispopulated
|
||||
----------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
|
@ -1347,7 +1347,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
|
||||
| pg_get_userbyid(c.relowner) AS matviewowner, +
|
||||
| t.spcname AS tablespace, +
|
||||
| c.relhasindex AS hasindexes, +
|
||||
| pg_relation_is_scannable(c.oid) AS isscannable, +
|
||||
| c.relispopulated AS ispopulated, +
|
||||
| pg_get_viewdef(c.oid) AS definition +
|
||||
| FROM ((pg_class c +
|
||||
| LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) +
|
||||
|
@ -15,10 +15,10 @@ SELECT * FROM tv ORDER BY type;
|
||||
EXPLAIN (costs off)
|
||||
CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
|
||||
CREATE MATERIALIZED VIEW tm AS SELECT type, sum(amt) AS totamt FROM t GROUP BY type WITH NO DATA;
|
||||
SELECT pg_relation_is_scannable('tm'::regclass);
|
||||
SELECT relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
|
||||
SELECT * FROM tm;
|
||||
REFRESH MATERIALIZED VIEW tm;
|
||||
SELECT pg_relation_is_scannable('tm'::regclass);
|
||||
SELECT relispopulated FROM pg_class WHERE oid = 'tm'::regclass;
|
||||
CREATE UNIQUE INDEX tm_type ON tm (type);
|
||||
SELECT * FROM tm;
|
||||
|
||||
@ -109,7 +109,7 @@ CREATE VIEW v_test2 AS SELECT moo, 2*moo FROM v_test1 UNION ALL SELECT moo, 3*mo
|
||||
CREATE MATERIALIZED VIEW mv_test2 AS SELECT moo, 2*moo FROM v_test2 UNION ALL SELECT moo, 3*moo FROM v_test2;
|
||||
\d+ mv_test2
|
||||
CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345;
|
||||
SELECT pg_relation_is_scannable('mv_test3'::regclass);
|
||||
SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass;
|
||||
|
||||
DROP VIEW v_test1 CASCADE;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user