check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
/*
- * Quietly ignore the request if the a materialized view is not scannable.
- * No harm is done because there is nothing no data to deal with, and we
- * don't want to throw an error if this is part of a multi-relation
- * request -- for example, CLUSTER was run on the entire database.
+ * Quietly ignore the request if this is a materialized view which has not
+ * been populated from its query. No harm is done because there is no data
+ * to deal with, and we don't want to throw an error if this is part of a
+ * multi-relation request -- for example, CLUSTER was run on the entire
+ * database.
*/
if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
- !OldHeap->rd_isscannable)
+ !OldHeap->rd_ispopulated)
{
relation_close(OldHeap, AccessExclusiveLock);
return;
if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW)
/* Make sure the heap looks good even if no rows are written. */
- SetRelationIsScannable(NewHeap);
+ SetMatViewToPopulated(NewHeap);
/*
* Scan through the OldHeap, either in OldIndex order or sequentially;
if (into->relkind == RELKIND_MATVIEW && !into->skipData)
/* Make sure the heap looks good even if no rows are written. */
- SetRelationIsScannable(intoRelationDesc);
+ SetMatViewToPopulated(intoRelationDesc);
/*
* Check INSERT permission on the constructed table.
const char *queryString);
/*
- * SetRelationIsScannable
- * Make the relation appear scannable.
+ * SetMatViewToPopulated
+ * Indicate that the materialized view has been populated by its query.
*
- * NOTE: This is only implemented for materialized views. The heap starts out
- * in a state that doesn't look scannable, and can only transition from there
- * to scannable, unless a new heap is created.
+ * 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.
*
* NOTE: caller must be holding an appropriate lock on the relation.
*/
void
-SetRelationIsScannable(Relation relation)
+SetMatViewToPopulated(Relation relation)
{
Page page;
Assert(relation->rd_rel->relkind == RELKIND_MATVIEW);
- Assert(relation->rd_isscannable == false);
+ Assert(relation->rd_ispopulated == false);
page = (Page) palloc(BLCKSZ);
PageInit(page, BLCKSZ, 0);
myState->hi_options |= HEAP_INSERT_SKIP_WAL;
myState->bistate = GetBulkInsertState();
- SetRelationIsScannable(transientrel);
+ SetMatViewToPopulated(transientrel);
/* Not using WAL requires smgr_targblock be initially invalid */
Assert(RelationGetTargetBlock(transientrel) == InvalidBlockNumber);
* Check that relations which are to be accessed are in a scannable
* state.
*
- * If not, throw error. For a materialized view, suggest refresh.
+ * Currently the only relations which are not are materialized views which
+ * have not been populated by their queries.
*/
static void
ExecCheckRelationsScannable(List *rangeTable)
if (rte->rtekind != RTE_RELATION)
continue;
- if (!RelationIdIsScannable(rte->relid))
- {
- if (rte->relkind == RELKIND_MATVIEW)
- {
- /* It is OK to replace the contents of an invalid matview. */
- if (rte->isResultRel)
- continue;
+ if (rte->relkind != RELKIND_MATVIEW)
+ continue;
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("materialized view \"%s\" has not been populated",
- get_rel_name(rte->relid)),
- errhint("Use the REFRESH MATERIALIZED VIEW command.")));
- }
- else
- /* This should never happen, so elog will do. */
- elog(ERROR, "relation \"%s\" is not flagged as scannable",
- get_rel_name(rte->relid));
- }
+ /* It is OK to target an unpopulated materialized for results. */
+ if (rte->isResultRel)
+ continue;
+
+ if (!RelationIdIsScannable(rte->relid))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("materialized view \"%s\" has not been populated",
+ get_rel_name(rte->relid)),
+ errhint("Use the REFRESH MATERIALIZED VIEW command.")));
}
}
/*
- * Tells whether a relation is scannable.
+ * Tells whether a relation is scannable based on its OID.
+ *
+ * Currently only non-populated materialized views are not. This is likely to
+ * change to include other conditions.
*
- * Currently only non-populated materialzed views are not.
+ * This should only be called while a lock is held on the relation.
*/
static bool
RelationIdIsScannable(Oid relid)
Relation relation;
bool result;
- relation = RelationIdGetRelation(relid);
- result = relation->rd_isscannable;
- RelationClose(relation);
+ relation = heap_open(relid, NoLock);
+ result = RelationIsScannable(relation);
+ heap_close(relation, NoLock);
return result;
}
/*
* Unless we are creating a view or are creating a materialized view WITH
- * NO DATA, ensure that all referenced relations are scannable.
+ * NO DATA, ensure that all referenced relations are scannable. The
+ * omitted cases will be checked as SELECT statements in a different
+ * phase, so checking again here would be wasteful and it would generate
+ * errors on a materialized view referenced as a target.
+ *
+ * NB: This is being done after all relations are locked, files have been
+ * opened, etc., to avoid duplicating that effort or creating deadlock
+ * possibilities.
*/
if ((eflags & EXEC_FLAG_WITH_NO_DATA) == 0)
ExecCheckRelationsScannable(rangeTable);
* expansion doesn't give us a lot to work with, so we are trusting
* earlier validations to throw error if needed.
*/
- if (rel->rd_rel->relkind == RELKIND_MATVIEW && rel->rd_isscannable)
+ if (rel->rd_rel->relkind == RELKIND_MATVIEW &&
+ RelationIsScannable(rel))
{
heap_close(rel, NoLock);
continue;
* Indicate whether a relation is scannable.
*
* Currently, this is always true except for a materialized view which has not
- * been populated.
+ * 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)
bool result;
relid = PG_GETARG_OID(0);
- relation = RelationIdGetRelation(relid);
- result = relation->rd_isscannable;
- RelationClose(relation);
+ relation = try_relation_open(relid, AccessShareLock);
+ if (relation == NULL)
+ PG_RETURN_BOOL(false);
+
+ result = RelationIsScannable(relation);
+
+ relation_close(relation, AccessShareLock);
PG_RETURN_BOOL(result);
}
if (relation->rd_rel->relkind == RELKIND_MATVIEW &&
heap_is_matview_init_state(relation))
- relation->rd_isscannable = false;
+ relation->rd_ispopulated = false;
else
- relation->rd_isscannable = true;
+ relation->rd_ispopulated = true;
/*
* now we can free the memory allocated for pg_class_tuple
* initialize physical addressing information for the relation
*/
RelationInitPhysicalAddr(relation);
- relation->rd_isscannable = true;
+ relation->rd_ispopulated = true;
/*
* initialize the rel-has-index flag, using hardwired knowledge
heap_freetuple(pg_class_tuple);
/* We must recalculate physical address in case it changed */
RelationInitPhysicalAddr(relation);
- relation->rd_isscannable = true;
+ relation->rd_ispopulated = true;
/*
* For a non-system index, there are fields of the pg_index row that are
RelationInitPhysicalAddr(relation);
if (relation->rd_rel->relkind == RELKIND_MATVIEW &&
heap_is_matview_init_state(relation))
- relation->rd_isscannable = false;
+ relation->rd_ispopulated = false;
else
- relation->rd_isscannable = true;
+ relation->rd_ispopulated = true;
if (relation->rd_rel->relkind == RELKIND_INDEX)
{
/* materialized view not initially scannable */
if (relkind == RELKIND_MATVIEW)
- rel->rd_isscannable = false;
+ rel->rd_ispopulated = false;
else
- rel->rd_isscannable = true;
+ rel->rd_ispopulated = true;
/*
* Okay to insert into the relcache hash tables.
RelationInitPhysicalAddr(rel);
if (rel->rd_rel->relkind == RELKIND_MATVIEW &&
heap_is_matview_init_state(rel))
- rel->rd_isscannable = false;
+ rel->rd_ispopulated = false;
else
- rel->rd_isscannable = true;
+ rel->rd_ispopulated = true;
}
/*
"c.relhasindex, c.relhasrules, c.relhasoids, "
"c.relfrozenxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
- "c.relpersistence, pg_relation_is_scannable(c.oid) as isscannable, "
+ "c.relpersistence, "
+ "CASE WHEN c.relkind = '%c' THEN pg_relation_is_scannable(c.oid) ELSE 't'::bool END as isscannable, "
"c.relpages, "
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
"d.refobjid AS owning_tab, "
"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,
#include "utils/relcache.h"
-extern void SetRelationIsScannable(Relation relation);
+extern void SetMatViewToPopulated(Relation relation);
extern void ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
ParamListInfo params, char *completionTag);
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_isscannable; /* rel can be scanned */
+ 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 */
((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP && \
!(relation)->rd_islocaltemp)
+
+/*
+ * RelationIsScannable
+ * Currently can only be false for a materialized view which has not been
+ * 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)
+
+
/* routines in utils/cache/relcache.c */
extern void RelationIncrementReferenceCount(Relation rel);
extern void RelationDecrementReferenceCount(Relation rel);