Skip to content

Commit cc1965a

Browse files
committed
Add support for REFRESH MATERIALIZED VIEW CONCURRENTLY.
This allows reads to continue without any blocking while a REFRESH runs. The new data appears atomically as part of transaction commit. Review questioned the Assert that a matview was not a system relation. This will be addressed separately. Reviewed by Hitoshi Harada, Robert Haas, Andres Freund. Merged after review with security patch f3ab5d4.
1 parent 7f7485a commit cc1965a

File tree

16 files changed

+646
-59
lines changed

16 files changed

+646
-59
lines changed

doc/src/sgml/mvcc.sgml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,7 @@ ERROR: could not serialize access due to read/write dependencies among transact
928928
</para>
929929

930930
<para>
931-
This lock mode is not automatically acquired on tables by any
932-
<productname>PostgreSQL</productname> command.
931+
Acquired by <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>.
933932
</para>
934933
</listitem>
935934
</varlistentry>

doc/src/sgml/ref/refresh_materialized_view.sgml

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PostgreSQL documentation
2121

2222
<refsynopsisdiv>
2323
<synopsis>
24-
REFRESH MATERIALIZED VIEW <replaceable class="PARAMETER">name</replaceable>
24+
REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] <replaceable class="PARAMETER">name</replaceable>
2525
[ WITH [ NO ] DATA ]
2626
</synopsis>
2727
</refsynopsisdiv>
@@ -38,12 +38,44 @@ REFRESH MATERIALIZED VIEW <replaceable class="PARAMETER">name</replaceable>
3838
data is generated and the materialized view is left in an unscannable
3939
state.
4040
</para>
41+
<para>
42+
<literal>CONCURRENTLY</literal> and <literal>WITH NO DATA</literal> may not
43+
be specified together.
44+
</para>
4145
</refsect1>
4246

4347
<refsect1>
4448
<title>Parameters</title>
4549

4650
<variablelist>
51+
<varlistentry>
52+
<term><literal>CONCURRENTLY</literal></term>
53+
<listitem>
54+
<para>
55+
Refresh the materialized view without locking out concurrent selects on
56+
the materialized view. Without this option a refresh which affects a
57+
lot of rows will tend to use fewer resources and complete more quickly,
58+
but could block other connections which are trying to read from the
59+
materialized view. This option may be faster in cases where a small
60+
number of rows are affected.
61+
</para>
62+
<para>
63+
This option is only allowed if there is at least one
64+
<literal>UNIQUE</literal> index on the materialized view which uses only
65+
column names and includes all rows; that is, it must not index on any
66+
expressions nor include a <literal>WHERE</literal> clause.
67+
</para>
68+
<para>
69+
This option may not be used when the materialized view is not already
70+
populated.
71+
</para>
72+
<para>
73+
Even with this option only one <literal>REFRESH</literal> at a time may
74+
run against any one materialized view.
75+
</para>
76+
</listitem>
77+
</varlistentry>
78+
4779
<varlistentry>
4880
<term><replaceable class="PARAMETER">name</replaceable></term>
4981
<listitem>

src/backend/commands/cluster.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
589589
heap_close(OldHeap, NoLock);
590590

591591
/* Create the transient table that will receive the re-ordered data */
592-
OIDNewHeap = make_new_heap(tableOid, tableSpace);
592+
OIDNewHeap = make_new_heap(tableOid, tableSpace, false,
593+
AccessExclusiveLock);
593594

594595
/* Copy the heap data into the new table in the desired order */
595596
copy_heap_data(OIDNewHeap, tableOid, indexOid,
@@ -616,7 +617,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
616617
* data, then call finish_heap_swap to complete the operation.
617618
*/
618619
Oid
619-
make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
620+
make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
621+
LOCKMODE lockmode)
620622
{
621623
TupleDesc OldHeapDesc;
622624
char NewHeapName[NAMEDATALEN];
@@ -626,8 +628,10 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
626628
HeapTuple tuple;
627629
Datum reloptions;
628630
bool isNull;
631+
Oid namespaceid;
632+
char relpersistence;
629633

630-
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
634+
OldHeap = heap_open(OIDOldHeap, lockmode);
631635
OldHeapDesc = RelationGetDescr(OldHeap);
632636

633637
/*
@@ -648,6 +652,17 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
648652
if (isNull)
649653
reloptions = (Datum) 0;
650654

655+
if (forcetemp)
656+
{
657+
namespaceid = LookupCreationNamespace("pg_temp");
658+
relpersistence = RELPERSISTENCE_TEMP;
659+
}
660+
else
661+
{
662+
namespaceid = RelationGetNamespace(OldHeap);
663+
relpersistence = OldHeap->rd_rel->relpersistence;
664+
}
665+
651666
/*
652667
* Create the new heap, using a temporary name in the same namespace as
653668
* the existing table. NOTE: there is some risk of collision with user
@@ -663,16 +678,16 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
663678
snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
664679

665680
OIDNewHeap = heap_create_with_catalog(NewHeapName,
666-
RelationGetNamespace(OldHeap),
681+
namespaceid,
667682
NewTableSpace,
668683
InvalidOid,
669684
InvalidOid,
670685
InvalidOid,
671686
OldHeap->rd_rel->relowner,
672687
OldHeapDesc,
673688
NIL,
674-
OldHeap->rd_rel->relkind,
675-
OldHeap->rd_rel->relpersistence,
689+
RELKIND_RELATION,
690+
relpersistence,
676691
false,
677692
RelationIsMapped(OldHeap),
678693
true,

0 commit comments

Comments
 (0)