Unlogged materialized views
Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.
You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:
docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253126psql -h localhost -U postgresBuilt from patchset v8 (message #8), October 14, 2026 at 03:40 PM.
Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:
git clone --branch t253126_8 https://github.com/hackorum-dev/postgres.gitIn a checkout you already have, add the fork once:
git remote add hackorum https://github.com/hackorum-dev/postgres.gitthen, for this patchset and every later one:
git fetch hackorum t253126_8 && git checkout t253126_8Patchset v8 (message #8) is on t253126_8
Hello!
I like to bring up supporting unlogged materialized views again. I am
aware that there were several discussions about them in the past, but
all of those seem quite old, the last one is from 7 years ago[1]/messages/by-id/CAKLmikNZ6qquFkh_eF9kWm5daTJy0PXhNt-xHT+iniYfaKkgZg@mail.gmail.com.
There are also two other related discussion with recent activity:
* incremental view maintenance[2]/messages/by-id/CAE8JnxMPbVtHgApfQ2Gny-a+nbVQN+=Zm4-yhsY7EJTFWVm1nQ@mail.gmail.com
* support for WHERE clauses for materialized view refresh[3]/messages/by-id/CAMjNa7eFzTQ5=oZMQiB2bMkez5KP4A77JC7SRjeVEkOrh7cUHw@mail.gmail.com
The reasoning behind all of these is probably similar as my root cause
for this proposal: refresh materialized view on huge views is slow and
results in wal churn. The above patches aim to make the changes
smaller, while I'd like to revisit the possibility of making
materialized views unlogged to avoid wal completely.
In some scenarios, crash-safety isn't a requirement for them, as it is
okay if certain features aren't available until a materialized view
rebuilds, or if queries complete slower until it does. Unlogged
materialized views would be perfect for this.
The general consensus in the old threads seems to be that unlogged
materialized views should become unpopulated after a crash instead of
empty, and that's also my assessment. The issue was and is just the
how:
pg_class currently has a relispopulated field: 0 for unpopulated
materialized views, 1 otherwise. With this approach, making them
unpopulated at or after recovery would require catalog changes during
recovery, which isn't possible.
That's why my suggestion is an alternative approach: refactor this field:
* rename it to relpopulated to avoid confusion (relispopulated becomes
a helper function)
* make it an integer: 1 for anything logged that is populated, even
normal materialized views, 0 for unpopulated logged materialized
views, and a special epoch counter for unlogged materialized views
That counter is a "concatenation" of two things: the timelineid, and a
newly introduced pg_control field, the unlogged reset generation
counter (unloggedResetGen): starts at 0, increases by 1 every time
during crash recovery.
When an unlogged materialized view is populated, we write the current
counter to pg_class.relpopulated. When we use it, we check if the
stored epoch counter is the same as the current one for the server -
if not, it's empty. We have to also disable access during recovery.
With that, standby behavior, replica promotion, crash recovery, and
everything else I tested seem to work as expected.
In my initial patchset I didn't address two possible improvements, but
I want to mention them:
* automatically refreshing unlogged views after crash recovery in a
background worker
* providing a health-check function that tells users if all unlogged
materialized views were already refreshed
These could be implemented either in core/contrib or as independent extensions.
Please see the attached patchset, what do you think about this approach?
[1]: /messages/by-id/CAKLmikNZ6qquFkh_eF9kWm5daTJy0PXhNt-xHT+iniYfaKkgZg@mail.gmail.com
[2]: /messages/by-id/CAE8JnxMPbVtHgApfQ2Gny-a+nbVQN+=Zm4-yhsY7EJTFWVm1nQ@mail.gmail.com
[3]: /messages/by-id/CAMjNa7eFzTQ5=oZMQiB2bMkez5KP4A77JC7SRjeVEkOrh7cUHw@mail.gmail.com
Attachments:
t253126_10001-Add-durable-unlogged-reset-generation-counter.patchapplication/octet-stream; name=0001-Add-durable-unlogged-reset-generation-counter.patchDownload+59-2
0003-Add-unlogged-materialized-views.patchapplication/octet-stream; name=0003-Add-unlogged-materialized-views.patchDownload+953-20
0002-Replace-pg_class.relispopulated-with-epoch-capable-r.patchapplication/octet-stream; name=0002-Replace-pg_class.relispopulated-with-epoch-capable-r.patchDownload+205-61
Hi Zsolt,
I like to bring up supporting unlogged materialized views again.
+1 from me. Most of the MVs I've built over the years were:
denormalized lookup tables behind a search screen, pre-aggregated
rollups for reporting, cached joins that were too slow to run per
request.
Losing some of those to a crash would have been fine as refresh
time for them wasn't a huge issue upon recovery.
* automatically refreshing unlogged views after crash recovery in a
background worker
Something still has to notice and rebuild, and right now that would be on
every user to write themselves. It would be nice to have this built-in IMO.
Thanks,
Adam
Hey Zsolt,
+1
Will take a look at the patch
Thanks,
Dharin
On Mon, Aug 10, 2026 at 9:13 PM Adam Brusselback <adambrusselback@gmail.com>
wrote:
Show quoted text
Hi Zsolt,
I like to bring up supporting unlogged materialized views again.
+1 from me. Most of the MVs I've built over the years were:
denormalized lookup tables behind a search screen, pre-aggregated
rollups for reporting, cached joins that were too slow to run per
request.
Losing some of those to a crash would have been fine as refresh
time for them wasn't a huge issue upon recovery.* automatically refreshing unlogged views after crash recovery in a
background workerSomething still has to notice and rebuild, and right now that would be on
every user to write themselves. It would be nice to have this built-in IMO.Thanks,
Adam
Thanks for the feedback!
Something still has to notice and rebuild, and right now that would be on
every user to write themselves. It would be nice to have this built-in IMO.
I agree that this would be more useful in core or in contrib, and I can implement it as part of this patch or as a follow up, but before that, I'd like some feedback if people agree with the current approach or not, as with a different approach we might need a completely different solution for that.
Hello!
I attached a rebased version, otherwise there are no changes.
On Tue, Aug 11, 2026 at 10:08 AM Zsolt Parragi
<zsolt.parragi@percona.com> wrote:
Show quoted text
Thanks for the feedback!
Something still has to notice and rebuild, and right now that would be on
every user to write themselves. It would be nice to have this built-in IMO.I agree that this would be more useful in core or in contrib, and I can implement it as part of this patch or as a follow up, but before that, I'd like some feedback if people agree with the current approach or not, as with a different approach we might need a completely different solution for that.
Attachments:
t253126_50003-Add-unlogged-materialized-views.patchapplication/octet-stream; name=0003-Add-unlogged-materialized-views.patchDownload+953-20
0001-Add-durable-unlogged-reset-generation-counter.patchapplication/octet-stream; name=0001-Add-durable-unlogged-reset-generation-counter.patchDownload+59-2
0002-Replace-pg_class.relispopulated-with-epoch-capable-r.patchapplication/octet-stream; name=0002-Replace-pg_class.relispopulated-with-epoch-capable-r.patchDownload+207-62
Hi Zsolt
On 19/08/2026 14:17, Zsolt Parragi wrote:
I attached a rebased version, otherwise there are no changes.
While doing some research for a new project (REFRESH ALL MATERIALIZED
VIEWS) I found this patch. 0002 and 0003 were not applying due to the
latest changes in meson.build and catversion.h. In order to test the
feature I had to rebase them -- PFA the rebased 0002 and 0003.
A few initial comments:
== The docs do not compile ==
Apparently the new ids in varlistentry, which are currently not used,
are breaking the file.
generating docs ...
ERROR: id attribute missing on <varlistentry> element under /book[@id =
'postgres']/part[@id = 'reference']/reference[@id =
'sql-commands']/refentry[@id =
'sql-altermaterializedview']/refsect1/variablelist
no result for postgres-full.xml
make[3]: *** [Makefile:130: html-stamp] Error 10
make[2]: *** [Makefile:8: html] Error 2
make[1]: *** [Makefile:16: html] Error 2
make: *** [GNUmakefile:27: html] Error 2
== tab completion ==
The tab completion does not suggest the new flag:
CREATE UNLOGGED <TAB>
SEQUENCE TABLE
== \d output ==
\d shows an unlogged matview as a normal one
postgres=# CREATE UNLOGGED MATERIALIZED VIEW umv1 AS SELECT 1;
SELECT 1
postgres=# \d umv1
Materialized view "public.umv1"
Column | Type | Collation | Nullable | Default
----------+---------+-----------+----------+---------
?column? | integer | | |
A unlogged table is shown as unlogged, so I believe this should also
apply to matviews:
postgres=# CREATE UNLOGGED TABLE ut1 AS SELECT 1;
SELECT 1
postgres=# \d ut1
Unlogged table "public.ut1"
Column | Type | Collation | Nullable | Default
----------+---------+-----------+----------+---------
?column? | integer | | |
PFA 004 with a few suggestions for the above mentioned issues. I'll take
a closer look at the code in the coming week.
Best, Jim
Attachments:
t253126_6v3-0001-Add-durable-unlogged-reset-generation-counter.patchtext/x-patch; charset=UTF-8; name=v3-0001-Add-durable-unlogged-reset-generation-counter.patchDownload+59-2
v3-0002-Replace-pg_class.relispopulated-with-epoch-capabl.patchtext/x-patch; charset=UTF-8; name=v3-0002-Replace-pg_class.relispopulated-with-epoch-capabl.patchDownload+207-62
v3-0003-Add-unlogged-materialized-views.patchtext/x-patch; charset=UTF-8; name=v3-0003-Add-unlogged-materialized-views.patchDownload+953-20
v3-0004-Minor-fixes-to-docs-tab-completion-and-psql-descr.patchtext/x-patch; charset=UTF-8; name=v3-0004-Minor-fixes-to-docs-tab-completion-and-psql-descr.patchDownload+24-14
Thank you for the feedback and suggestions!
I rebased again; the patch no longer increments the
controlfile/catalog version to make conflicts less likely.
I included the changes from 0004, and added a few other minor changes/fixes.
I also found another issue while testing replication scenarios. To fix
that, the counter is now based on the LSN, not the timeline, this is
the most significant change compared to v3.
Another open question I want to point out is that I found a pg_dump
failure with --no-unlogged-table-data. For now I chose the option
where this flag doesn't affect materialized views, as they are not
tables, but I am not sure if this is the proper solution.
Attachments:
t253126_7v4-0002-Replace-pg_class.relispopulated-with-epoch-capabl.patchapplication/octet-stream; name=v4-0002-Replace-pg_class.relispopulated-with-epoch-capabl.patchDownload+206-61
v4-0001-Add-durable-unlogged-reset-LSN-to-pg_control.patchapplication/octet-stream; name=v4-0001-Add-durable-unlogged-reset-LSN-to-pg_control.patchDownload+67-1
v4-0003-Add-unlogged-materialized-views.patchapplication/octet-stream; name=v4-0003-Add-unlogged-materialized-views.patchDownload+886-43
v5 is a trivial rebase changing tap test numbers.
Attachments:
t253126_8v5-0003-Add-unlogged-materialized-views.patchapplication/octet-stream; name=v5-0003-Add-unlogged-materialized-views.patchDownload+886-43
v5-0002-Replace-pg_class.relispopulated-with-epoch-capabl.patchapplication/octet-stream; name=v5-0002-Replace-pg_class.relispopulated-with-epoch-capabl.patchDownload+206-61
v5-0001-Add-durable-unlogged-reset-LSN-to-pg_control.patchapplication/octet-stream; name=v5-0001-Add-durable-unlogged-reset-LSN-to-pg_control.patchDownload+67-1
On 09/10/2026 13:40, Zsolt Parragi wrote:
v5 is a trivial rebase changing tap test numbers.
Today I finally found some time revisit this patch.
== no relispopulated in pg_matviews ==
Client tools like pgAdmin rely on this column to display matview info:
diff --git a/src/backend/catalog/system_views.sql
b/src/backend/catalog/system_views.sql
index ddf801307eb..724a23d559f 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)
With the patch applied, pgAdmin can no longer display the properties of
a matview:
column c.relispopulated does not exist
LINE 6: c.relispopulated AS with_data,
^
HINT: Perhaps you meant to reference the column "c.relpopulated".
I will take a closer look at the code in the next few days.
Best, Jim
== no relispopulated in pg_matviews ==
Client tools like pgAdmin rely on this column to display matview info:
There is (or rather: there wasn't even before), the column is called pg_matview.ispopulated and it is still there
column c.relispopulated does not exist
LINE 6: c.relispopulated AS with_data,
^
HINT: Perhaps you meant to reference the column "c.relpopulated".
What is missing is the relispopulated boolean from pg_class, and I don't see how that could stay with this approach.
pg_class.relispopulated could only stay if we could update it after recovery, but before accepting connections (as otherwise it could become stale for unlogged matviews), and I don't see an easy way to do that, that's why I implemented this patch in a different way.
Otherwise, it could become stale, that's why the patch removes it.
But looking at pgadmin, it would be easy to patch it in a way that works with both all earlier versions and this patch.