From 6b340f56272eb1c667255882a8f50e0199cf3429 Mon Sep 17 00:00:00 2001 From: Zsolt Parragi Date: Sun, 6 Sep 2026 21:03:48 +0000 Subject: [PATCH v5 2/3] Replace pg_class.relispopulated with epoch-capable relpopulated Retype the bool relispopulated to an int64 relpopulated: 0 means no data, 1 means crash-safe populated (every relation except unlogged matviews), anything else is an epoch stamp recording when an unlogged matview was populated. The read side moves to RelationIsPopulated() in matview.c, replacing the old rel.h macros. Rename rather than reuse the column so external queries fail loudly. Add pg_matview_is_populated(regclass), used by pg_matviews. pg_dump reads relpopulated <> 0 and, in binary upgrade, calls binary_upgrade_set_matview_populated() instead of poking pg_class. This changes the catalogs, so a catversion bump is needed; it is left out here to keep the patch from conflicting on every rebase. --- doc/src/sgml/catalogs.sgml | 27 +++++---- doc/src/sgml/func/func-info.sgml | 16 ++++++ src/backend/catalog/heap.c | 2 +- src/backend/catalog/system_views.sql | 2 +- src/backend/commands/copyto.c | 1 + src/backend/commands/matview.c | 64 +++++++++++++++++++++- src/backend/commands/repack.c | 1 + src/backend/executor/execUtils.c | 3 +- src/backend/utils/adt/pg_upgrade_support.c | 20 +++++++ src/backend/utils/cache/relcache.c | 8 +-- src/bin/pg_dump/pg_dump.c | 39 +++++++++---- src/include/catalog/pg_class.h | 18 ++++-- src/include/catalog/pg_proc.dat | 8 +++ src/include/commands/matview.h | 2 + src/include/utils/rel.h | 16 ------ src/test/regress/expected/matview.out | 31 ++++++++--- src/test/regress/expected/rules.out | 2 +- src/test/regress/sql/matview.sql | 9 ++- 18 files changed, 206 insertions(+), 63 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 78cce7e370d..f2b7dd9ef0e 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -2228,16 +2228,6 @@ SCRAM-SHA-256$<iteration count>:&l - - - relispopulated bool - - - True if relation is populated (this is true for all - relations other than some materialized views) - - - relreplident char @@ -2301,6 +2291,23 @@ SCRAM-SHA-256$<iteration count>:&l + + + relpopulated int8 + + + Populated epoch. Zero if the relation does not hold data (a + materialized view created or refreshed WITH NO DATA). + One if the relation is populated with crash-safe storage; this is the + value for every relation other than a materialized view, and for + populated permanent materialized views. Any other value is an epoch + stamp recording when an unlogged materialized view was populated; its + data is valid only while the cluster remains in that epoch. Use + pg_matview_is_populated instead of reading this + column directly. + + + relacl aclitem[] diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml index 8c88865cda2..9b4cca0fbaf 100644 --- a/doc/src/sgml/func/func-info.sgml +++ b/doc/src/sgml/func/func-info.sgml @@ -1867,6 +1867,22 @@ SELECT currval(pg_get_serial_sequence('sometable', 'id')); + + + + pg_matview_is_populated + + pg_matview_is_populated ( regclass ) + boolean + + + Returns true if the materialized view currently holds valid data, + false if it must be refreshed before use. Returns + NULL for arguments that are not materialized + views. + + + diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index f0a159c1b77..82898ce0641 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -966,12 +966,12 @@ InsertPgClassTuple(Relation pg_class_desc, values[Anum_pg_class_relrowsecurity - 1] = BoolGetDatum(rd_rel->relrowsecurity); values[Anum_pg_class_relforcerowsecurity - 1] = BoolGetDatum(rd_rel->relforcerowsecurity); values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass); - values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated); values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident); values[Anum_pg_class_relispartition - 1] = BoolGetDatum(rd_rel->relispartition); values[Anum_pg_class_relrewrite - 1] = ObjectIdGetDatum(rd_rel->relrewrite); values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid); values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid); + values[Anum_pg_class_relpopulated - 1] = Int64GetDatum(rd_rel->relpopulated); if (relacl != (Datum) 0) values[Anum_pg_class_relacl - 1] = relacl; else diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 809b9c0f1e4..2acf1c8dd9d 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -150,7 +150,7 @@ CREATE VIEW pg_matviews AS pg_get_userbyid(C.relowner) AS matviewowner, T.spcname AS tablespace, C.relhasindex AS hasindexes, - C.relispopulated AS ispopulated, + pg_matview_is_populated(C.oid) 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) diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index a187d8ec284..5b6e88f95e1 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -23,6 +23,7 @@ #include "access/tupconvert.h" #include "catalog/pg_inherits.h" #include "commands/copyapi.h" +#include "commands/matview.h" #include "commands/progress.h" #include "executor/execdesc.h" #include "executor/executor.h" diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index 40748958eaf..f8c18720e99 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -20,6 +20,7 @@ #include "access/multixact.h" #include "access/tableam.h" #include "access/xact.h" +#include "access/xlog.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_am.h" @@ -79,6 +80,7 @@ SetMatViewPopulatedState(Relation relation, bool newstate) { Relation pgrel; HeapTuple tuple; + Form_pg_class classform; Assert(relation->rd_rel->relkind == RELKIND_MATVIEW); @@ -94,7 +96,14 @@ SetMatViewPopulatedState(Relation relation, bool newstate) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(relation)); - ((Form_pg_class) GETSTRUCT(tuple))->relispopulated = newstate; + classform = (Form_pg_class) GETSTRUCT(tuple); + + if (!newstate) + classform->relpopulated = RELPOPULATED_NONE; + else if (relation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED) + classform->relpopulated = (int64) GetUnloggedPopulatedEpoch(); + else + classform->relpopulated = RELPOPULATED_ETERNAL; CatalogTupleUpdate(pgrel, &tuple->t_self, tuple); @@ -108,6 +117,59 @@ SetMatViewPopulatedState(Relation relation, bool newstate) CommandCounterIncrement(); } +/* + * MatViewPopulatedValueIsValid + * Does this pg_class.relpopulated value denote currently valid data? + * + * This only distinguishes RELPOPULATED_NONE from everything else; any other + * value, whether RELPOPULATED_ETERNAL or an epoch stamp, counts as valid. + */ +bool +MatViewPopulatedValueIsValid(int64 value) +{ + return value != RELPOPULATED_NONE; +} + +/* + * RelationIsPopulated + * Does this relation currently hold valid data? Only a materialized + * view can return false. + */ +bool +RelationIsPopulated(Relation relation) +{ + return MatViewPopulatedValueIsValid(relation->rd_rel->relpopulated); +} + +/* + * pg_matview_is_populated + * Does the materialized view currently hold valid data? + * + * Returns NULL if the argument is not a materialized view, or if it does + * not exist. + */ +Datum +pg_matview_is_populated(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + HeapTuple tuple; + Form_pg_class classform; + bool result; + + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + PG_RETURN_NULL(); + classform = (Form_pg_class) GETSTRUCT(tuple); + if (classform->relkind != RELKIND_MATVIEW) + { + ReleaseSysCache(tuple); + PG_RETURN_NULL(); + } + result = MatViewPopulatedValueIsValid(classform->relpopulated); + ReleaseSysCache(tuple); + PG_RETURN_BOOL(result); +} + /* * ExecRefreshMatView -- execute a REFRESH MATERIALIZED VIEW command * diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index a584e138ada..7103b67d5e3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -53,6 +53,7 @@ #include "catalog/pg_inherits.h" #include "catalog/toasting.h" #include "commands/defrem.h" +#include "commands/matview.h" #include "commands/progress.h" #include "commands/repack.h" #include "commands/repack_internal.h" diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index c0c4276acbf..3433e29cad8 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -49,6 +49,7 @@ #include "access/table.h" #include "access/tableam.h" #include "access/tupconvert.h" +#include "commands/matview.h" #include "executor/executor.h" #include "executor/nodeModifyTable.h" #include "jit/jit.h" @@ -778,7 +779,7 @@ ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags) * to do this, perhaps, but there is no better place. */ if ((eflags & (EXEC_FLAG_EXPLAIN_ONLY | EXEC_FLAG_WITH_NO_DATA)) == 0 && - !RelationIsScannable(rel)) + !RelationIsPopulated(rel)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("materialized view \"%s\" has not been populated", diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c index f5017022d2e..a081db3695d 100644 --- a/src/backend/utils/adt/pg_upgrade_support.c +++ b/src/backend/utils/adt/pg_upgrade_support.c @@ -19,6 +19,7 @@ #include "catalog/pg_subscription_rel.h" #include "catalog/pg_type.h" #include "commands/extension.h" +#include "commands/matview.h" #include "miscadmin.h" #include "replication/logical.h" #include "replication/logicallauncher.h" @@ -29,6 +30,7 @@ #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/pg_lsn.h" +#include "utils/rel.h" #define CHECK_IS_BINARY_UPGRADE \ @@ -192,6 +194,24 @@ binary_upgrade_set_next_pg_authid_oid(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +Datum +binary_upgrade_set_matview_populated(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + Relation rel; + + CHECK_IS_BINARY_UPGRADE; + + rel = relation_open(relid, AccessExclusiveLock); + if (rel->rd_rel->relkind != RELKIND_MATVIEW) + elog(ERROR, "relation \"%s\" is not a materialized view", + RelationGetRelationName(rel)); + SetMatViewPopulatedState(rel, true); + relation_close(rel, NoLock); + + PG_RETURN_VOID(); +} + Datum binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index d8f04a05309..9d34d26ee3d 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1947,7 +1947,7 @@ formrdesc(const char *relationName, Oid relationReltype, relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT; /* ... and they're always populated, too */ - relation->rd_rel->relispopulated = true; + relation->rd_rel->relpopulated = RELPOPULATED_ETERNAL; relation->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING; relation->rd_rel->relpages = 0; @@ -3661,10 +3661,8 @@ RelationBuildLocalRelation(const char *relname, } /* 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; + rel->rd_rel->relpopulated = (relkind == RELKIND_MATVIEW) ? + RELPOPULATED_NONE : RELPOPULATED_ETERNAL; /* set replica identity -- system catalogs and non-tables don't have one */ if (!IsCatalogNamespace(relnamespace) && diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 90971fbdac6..f9a53a4594d 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7249,8 +7249,26 @@ getTables(Archive *fout, int *numTables) appendPQExpBufferStr(query, "c.relhasoids, "); - appendPQExpBufferStr(query, - "c.relispopulated, "); + /* + * A normal dump repopulates matviews with REFRESH, so any nonzero + * relpopulated value (the user's intent to have the matview populated) + * should dump as populated. Binary upgrade instead transfers the heap + * storage as-is, so it must use the effective state: an unlogged matview + * whose epoch stamp went stale (storage reset by a crash and never + * refreshed) has to be restored as unpopulated, or the new cluster would + * present the transferred empty heap as valid data. + */ + if (fout->remoteVersion >= 200000 && fout->dopt->binary_upgrade) + appendPQExpBufferStr(query, + "(CASE WHEN c.relkind = " CppAsString2(RELKIND_MATVIEW) + " THEN pg_catalog.pg_matview_is_populated(c.oid) " + "ELSE true END) AS relispopulated, "); + else if (fout->remoteVersion >= 200000) + appendPQExpBufferStr(query, + "c.relpopulated <> 0 AS relispopulated, "); + else + appendPQExpBufferStr(query, + "c.relispopulated, "); appendPQExpBufferStr(query, "c.relreplident, "); @@ -17736,21 +17754,20 @@ dumpTableSchema(Archive *fout, const 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; in any case, - * we want to transfer the matview's heap storage, not run REFRESH. + * In binary_upgrade mode, restore matviews' populated status using + * the binary_upgrade_set_matview_populated() support function. We + * can't use REFRESH MATERIALIZED VIEW since it's possible that some + * underlying matview is not populated even though this matview is; + * in any case, we want to transfer the matview's heap storage, not + * run REFRESH. */ if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW && tbinfo->relispopulated) { appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n"); - appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n" - "SET relispopulated = 't'\n" - "WHERE oid = "); + appendPQExpBufferStr(q, "SELECT pg_catalog.binary_upgrade_set_matview_populated("); appendStringLiteralAH(q, qualrelname, fout); - appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); + appendPQExpBufferStr(q, "::pg_catalog.regclass);\n"); } /* diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 005b62b38c9..7644f57414d 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -115,9 +115,6 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat /* row security forced for owners or not */ bool relforcerowsecurity BKI_DEFAULT(f); - /* matview currently holds query results */ - bool relispopulated BKI_DEFAULT(t); - /* see REPLICA_IDENTITY_xxx constants */ char relreplident BKI_DEFAULT(n); @@ -133,6 +130,15 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat /* all multixacts in this rel are >= this; it is really a MultiXactId */ TransactionId relminmxid BKI_DEFAULT(1); /* FirstMultiXactId */ + /* + * Populated epoch. 0 (RELPOPULATED_NONE): no data. 1 + * (RELPOPULATED_ETERNAL): crash-safe populated, used for every relation + * except unlogged matviews. Any other value is an epoch stamp for a + * populated unlogged matview, valid only while the cluster stays in that + * epoch. See MatViewPopulatedValueIsValid(). + */ + int64 relpopulated BKI_DEFAULT(1); + #ifdef CATALOG_VARLEN /* variable-length fields start here */ /* NOTE: These fields are not present in a relcache entry's rd_rel field. */ /* access permissions */ @@ -150,7 +156,7 @@ END_CATALOG_STRUCT /* Size of fixed part of pg_class tuples, not counting var-length fields */ #define CLASS_TUPLE_SIZE \ - (offsetof(FormData_pg_class,relminmxid) + sizeof(TransactionId)) + (offsetof(FormData_pg_class,relpopulated) + sizeof(int64)) /* ---------------- * Form_pg_class corresponds to a pointer to a tuple with @@ -179,6 +185,10 @@ MAKE_SYSCACHE(RELNAMENSP, pg_class_relname_nsp_index, 128); #define RELKIND_PARTITIONED_TABLE 'p' /* partitioned table */ #define RELKIND_PARTITIONED_INDEX 'I' /* partitioned index */ +/* Reserved values of pg_class.relpopulated; any other value is an epoch. */ +#define RELPOPULATED_NONE 0 /* not populated */ +#define RELPOPULATED_ETERNAL 1 /* populated, storage is crash-safe */ + #define RELPERSISTENCE_PERMANENT 'p' /* regular table */ #define RELPERSISTENCE_UNLOGGED 'u' /* unlogged permanent table */ #define RELPERSISTENCE_TEMP 't' /* temporary table */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index f46427258e3..c5474c00579 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12034,6 +12034,10 @@ proname => 'binary_upgrade_set_next_pg_tablespace_oid', provolatile => 'v', proparallel => 'u', prorettype => 'void', proargtypes => 'oid', prosrc => 'binary_upgrade_set_next_pg_tablespace_oid' }, +{ oid => '8668', descr => 'for use by pg_upgrade', + proname => 'binary_upgrade_set_matview_populated', provolatile => 'v', + proparallel => 'u', prorettype => 'void', proargtypes => 'oid', + prosrc => 'binary_upgrade_set_matview_populated' }, { oid => '6312', descr => 'for use by pg_upgrade', proname => 'binary_upgrade_check_logical_slot_pending_wal', provolatile => 'v', proparallel => 'u', prorettype => 'pg_lsn', @@ -12402,6 +12406,10 @@ proname => 'pg_relation_is_publishable', provolatile => 's', prorettype => 'bool', proargtypes => 'regclass', prosrc => 'pg_relation_is_publishable' }, +{ oid => '8667', descr => 'is materialized view populated', + proname => 'pg_matview_is_populated', provolatile => 's', + prorettype => 'bool', proargtypes => 'regclass', + prosrc => 'pg_matview_is_populated' }, # rls { oid => '3298', diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h index 738c731c1a9..06096e43601 100644 --- a/src/include/commands/matview.h +++ b/src/include/commands/matview.h @@ -22,6 +22,8 @@ extern void SetMatViewPopulatedState(Relation relation, bool newstate); +extern bool MatViewPopulatedValueIsValid(int64 value); +extern bool RelationIsPopulated(Relation relation); extern ObjectAddress ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, QueryCompletion *qc); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 7b8c2b1e362..65a5933a26f 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -708,22 +708,6 @@ RelationCloseSmgr(Relation relation) !(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_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) - /* * RelationIsAccessibleInLogicalDecoding * True if we need to log enough information to have access via diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 0355720dfc6..7500bf027da 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -27,9 +27,9 @@ EXPLAIN (costs off) (3 rows) CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type WITH NO DATA; -SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; - relispopulated ----------------- +SELECT pg_matview_is_populated('mvtest_tm'::regclass); + pg_matview_is_populated +------------------------- f (1 row) @@ -37,12 +37,25 @@ SELECT * FROM mvtest_tm ORDER BY type; ERROR: materialized view "mvtest_tm" has not been populated HINT: Use the REFRESH MATERIALIZED VIEW command. REFRESH MATERIALIZED VIEW mvtest_tm; -SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; - relispopulated ----------------- +SELECT pg_matview_is_populated('mvtest_tm'::regclass); + pg_matview_is_populated +------------------------- t (1 row) +-- ... but NULL for a relation that is not a materialized view, or nonexistent +SELECT pg_matview_is_populated('mvtest_t'::regclass); + pg_matview_is_populated +------------------------- + +(1 row) + +SELECT pg_matview_is_populated(0); + pg_matview_is_populated +------------------------- + +(1 row) + CREATE UNIQUE INDEX mvtest_tm_type ON mvtest_tm (type); SELECT * FROM mvtest_tm ORDER BY type; type | totamt @@ -376,9 +389,9 @@ UNION ALL FROM mvtest_vt2; CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345; -SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass; - relispopulated ----------------- +SELECT pg_matview_is_populated('mv_test3'::regclass); + pg_matview_is_populated +------------------------- t (1 row) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 0addd043e68..5bc50b4fc66 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1414,7 +1414,7 @@ pg_matviews| SELECT n.nspname AS schemaname, pg_get_userbyid(c.relowner) AS matviewowner, t.spcname AS tablespace, c.relhasindex AS hasindexes, - c.relispopulated AS ispopulated, + pg_matview_is_populated((c.oid)::regclass) AS ispopulated, pg_get_viewdef(c.oid) AS definition FROM ((pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql index 934426b9ae8..8890a2f7932 100644 --- a/src/test/regress/sql/matview.sql +++ b/src/test/regress/sql/matview.sql @@ -15,10 +15,13 @@ SELECT * FROM mvtest_tv ORDER BY type; EXPLAIN (costs off) CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type WITH NO DATA; CREATE MATERIALIZED VIEW mvtest_tm AS SELECT type, sum(amt) AS totamt FROM mvtest_t GROUP BY type WITH NO DATA; -SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; +SELECT pg_matview_is_populated('mvtest_tm'::regclass); SELECT * FROM mvtest_tm ORDER BY type; REFRESH MATERIALIZED VIEW mvtest_tm; -SELECT relispopulated FROM pg_class WHERE oid = 'mvtest_tm'::regclass; +SELECT pg_matview_is_populated('mvtest_tm'::regclass); +-- ... but NULL for a relation that is not a materialized view, or nonexistent +SELECT pg_matview_is_populated('mvtest_t'::regclass); +SELECT pg_matview_is_populated(0); CREATE UNIQUE INDEX mvtest_tm_type ON mvtest_tm (type); SELECT * FROM mvtest_tm ORDER BY type; @@ -122,7 +125,7 @@ CREATE VIEW mvtest_vt2 AS SELECT moo, 2*moo FROM mvtest_vt1 UNION ALL SELECT moo CREATE MATERIALIZED VIEW mv_test2 AS SELECT moo, 2*moo FROM mvtest_vt2 UNION ALL SELECT moo, 3*moo FROM mvtest_vt2; \d+ mv_test2 CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345; -SELECT relispopulated FROM pg_class WHERE oid = 'mv_test3'::regclass; +SELECT pg_matview_is_populated('mv_test3'::regclass); DROP VIEW mvtest_vt1 CASCADE; -- 2.55.0