Skip to content

Commit 71d05a2

Browse files
committed
pg_visibility: Add pg_truncate_visibility_map function.
This requires some core changes as well so that we can properly WAL-log the truncation. Specifically, it changes the format of the XLOG_SMGR_TRUNCATE WAL record, so bump XLOG_PAGE_MAGIC. Patch by me, reviewed but not fully endorsed by Andres Freund.
1 parent 54f5c51 commit 71d05a2

File tree

8 files changed

+133
-13
lines changed

8 files changed

+133
-13
lines changed

contrib/pg_visibility/pg_visibility--1.0--1.1.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,12 @@ RETURNS SETOF tid
1313
AS 'MODULE_PATHNAME', 'pg_check_visible'
1414
LANGUAGE C STRICT;
1515

16+
CREATE FUNCTION pg_truncate_visibility_map(regclass)
17+
RETURNS void
18+
AS 'MODULE_PATHNAME', 'pg_truncate_visibility_map'
19+
LANGUAGE C STRICT
20+
PARALLEL UNSAFE; -- let's not make this any more dangerous
21+
1622
REVOKE ALL ON FUNCTION pg_check_frozen(regclass) FROM PUBLIC;
1723
REVOKE ALL ON FUNCTION pg_check_visible(regclass) FROM PUBLIC;
24+
REVOKE ALL ON FUNCTION pg_truncate_visibility_map(regclass) FROM PUBLIC;

contrib/pg_visibility/pg_visibility--1.1.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ RETURNS SETOF tid
5757
AS 'MODULE_PATHNAME', 'pg_check_visible'
5858
LANGUAGE C STRICT;
5959

60+
-- Truncate the visibility map fork.
61+
CREATE FUNCTION pg_truncate_visibility_map(regclass)
62+
RETURNS void
63+
AS 'MODULE_PATHNAME', 'pg_truncate_visibility_map'
64+
LANGUAGE C STRICT
65+
PARALLEL UNSAFE; -- let's not make this any more dangerous
66+
6067
-- Don't want these to be available to public.
6168
REVOKE ALL ON FUNCTION pg_visibility_map(regclass, bigint) FROM PUBLIC;
6269
REVOKE ALL ON FUNCTION pg_visibility(regclass, bigint) FROM PUBLIC;
@@ -65,3 +72,4 @@ REVOKE ALL ON FUNCTION pg_visibility(regclass) FROM PUBLIC;
6572
REVOKE ALL ON FUNCTION pg_visibility_map_summary(regclass) FROM PUBLIC;
6673
REVOKE ALL ON FUNCTION pg_check_frozen(regclass) FROM PUBLIC;
6774
REVOKE ALL ON FUNCTION pg_check_visible(regclass) FROM PUBLIC;
75+
REVOKE ALL ON FUNCTION pg_truncate_visibility_map(regclass) FROM PUBLIC;

contrib/pg_visibility/pg_visibility.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
#include "access/htup_details.h"
1212
#include "access/visibilitymap.h"
1313
#include "catalog/pg_type.h"
14+
#include "catalog/storage_xlog.h"
1415
#include "funcapi.h"
1516
#include "miscadmin.h"
1617
#include "storage/bufmgr.h"
1718
#include "storage/procarray.h"
19+
#include "storage/smgr.h"
1820
#include "utils/rel.h"
1921

2022
PG_MODULE_MAGIC;
@@ -40,6 +42,7 @@ PG_FUNCTION_INFO_V1(pg_visibility_rel);
4042
PG_FUNCTION_INFO_V1(pg_visibility_map_summary);
4143
PG_FUNCTION_INFO_V1(pg_check_frozen);
4244
PG_FUNCTION_INFO_V1(pg_check_visible);
45+
PG_FUNCTION_INFO_V1(pg_truncate_visibility_map);
4346

4447
static TupleDesc pg_visibility_tupdesc(bool include_blkno, bool include_pd);
4548
static vbits *collect_visibility_data(Oid relid, bool include_pd);
@@ -335,6 +338,75 @@ pg_check_visible(PG_FUNCTION_ARGS)
335338
SRF_RETURN_DONE(funcctx);
336339
}
337340

341+
/*
342+
* Remove the visibility map fork for a relation. If there turn out to be
343+
* any bugs in the visibility map code that require rebuilding the VM, this
344+
* provides users with a way to do it that is cleaner than shutting down the
345+
* server and removing files by hand.
346+
*
347+
* This is a cut-down version of RelationTruncate.
348+
*/
349+
Datum
350+
pg_truncate_visibility_map(PG_FUNCTION_ARGS)
351+
{
352+
Oid relid = PG_GETARG_OID(0);
353+
Relation rel;
354+
355+
rel = relation_open(relid, AccessExclusiveLock);
356+
357+
if (rel->rd_rel->relkind != RELKIND_RELATION &&
358+
rel->rd_rel->relkind != RELKIND_MATVIEW &&
359+
rel->rd_rel->relkind != RELKIND_TOASTVALUE)
360+
ereport(ERROR,
361+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
362+
errmsg("\"%s\" is not a table, materialized view, or TOAST table",
363+
RelationGetRelationName(rel))));
364+
365+
RelationOpenSmgr(rel);
366+
rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
367+
368+
visibilitymap_truncate(rel, 0);
369+
370+
if (RelationNeedsWAL(rel))
371+
{
372+
xl_smgr_truncate xlrec;
373+
374+
xlrec.blkno = 0;
375+
xlrec.rnode = rel->rd_node;
376+
xlrec.flags = SMGR_TRUNCATE_VM;
377+
378+
XLogBeginInsert();
379+
XLogRegisterData((char *) &xlrec, sizeof(xlrec));
380+
381+
XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE | XLR_SPECIAL_REL_UPDATE);
382+
}
383+
384+
/*
385+
* Release the lock right away, not at commit time.
386+
*
387+
* It would be a problem to release the lock prior to commit if this
388+
* truncate operation sends any transactional invalidation messages. Other
389+
* backends would potentially be able to lock the relation without
390+
* processing them in the window of time between when we release the lock
391+
* here and when we sent the messages at our eventual commit. However,
392+
* we're currently only sending a non-transactional smgr invalidation,
393+
* which will have been posted to shared memory immediately from within
394+
* visibilitymap_truncate. Therefore, there should be no race here.
395+
*
396+
* The reason why it's desirable to release the lock early here is because
397+
* of the possibility that someone will need to use this to blow away many
398+
* visibility map forks at once. If we can't release the lock until
399+
* commit time, the transaction doing this will accumulate
400+
* AccessExclusiveLocks on all of those relations at the same time, which
401+
* is undesirable. However, if this turns out to be unsafe we may have no
402+
* choice...
403+
*/
404+
relation_close(rel, AccessExclusiveLock);
405+
406+
/* Nothing to return. */
407+
PG_RETURN_VOID();
408+
}
409+
338410
/*
339411
* Helper function to construct whichever TupleDesc we need for a particular
340412
* call.

doc/src/sgml/pgvisibility.sgml

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99

1010
<para>
1111
The <filename>pg_visibility</> module provides a means for examining the
12-
visibility map (VM) and page-level visibility information.
12+
visibility map (VM) and page-level visibility information. It also
13+
provides functions to check the integrity of the visibility map and to
14+
force it to be rebuilt.
1315
</para>
1416

1517
<para>
16-
These routines return information about three different bits. The
17-
all-visible bit in the visibility map indicates that every tuple on
18-
a given page of a relation is visible to every current transaction. The
19-
all-frozen bit in the visibility map indicates that every tuple on the
18+
Three different bits are used to store information about page-level
19+
visibility. The all-visible bit in the visibility map indicates that every
20+
tuple on a given page of a relation is visible to every current transaction.
21+
The all-frozen bit in the visibility map indicates that every tuple on the
2022
page is frozen; that is, no future vacuum will need to modify the page
2123
until such time as a tuple is inserted, updated, deleted, or locked on
2224
that page. The page-level <literal>PD_ALL_VISIBLE</literal> bit has the
@@ -25,7 +27,8 @@
2527
will normally agree, but the page-level bit can sometimes be set while the
2628
visibility map bit is clear after a crash recovery; or they can disagree
2729
because of a change which occurs after <literal>pg_visibility</> examines
28-
the visibility map and before it examines the data page.
30+
the visibility map and before it examines the data page. Any event which
31+
causes data corruption can also cause these bits to disagree.
2932
</para>
3033

3134
<para>
@@ -118,6 +121,21 @@
118121
</para>
119122
</listitem>
120123
</varlistentry>
124+
125+
<varlistentry>
126+
<term><function>pg_truncate_visibility_map(regclass) returns void</function></term>
127+
128+
<listitem>
129+
<para>
130+
Truncates the visibility map for the given relation. This function
131+
is only expected to be useful if you suspect that the visibility map
132+
for the indicated relation is corrupt and wish to rebuild it. The first
133+
<command>VACUUM</> executed on the given relation after this function
134+
is executed will scan every page in the relation and rebuild the
135+
visibility map.
136+
</para>
137+
</listitem>
138+
</varlistentry>
121139
</variablelist>
122140

123141
<para>

src/backend/access/rmgrdesc/smgrdesc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ smgr_desc(StringInfo buf, XLogReaderState *record)
3737
xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec;
3838
char *path = relpathperm(xlrec->rnode, MAIN_FORKNUM);
3939

40-
appendStringInfo(buf, "%s to %u blocks", path, xlrec->blkno);
40+
appendStringInfo(buf, "%s to %u blocks flags %d", path,
41+
xlrec->blkno, xlrec->flags);
4142
pfree(path);
4243
}
4344
}

src/backend/catalog/storage.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
268268

269269
xlrec.blkno = nblocks;
270270
xlrec.rnode = rel->rd_node;
271+
xlrec.flags = SMGR_TRUNCATE_ALL;
271272

272273
XLogBeginInsert();
273274
XLogRegisterData((char *) &xlrec, sizeof(xlrec));
@@ -522,17 +523,22 @@ smgr_redo(XLogReaderState *record)
522523
*/
523524
XLogFlush(lsn);
524525

525-
smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno);
526+
if ((xlrec->flags & SMGR_TRUNCATE_HEAP) != 0)
527+
{
528+
smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno);
526529

527-
/* Also tell xlogutils.c about it */
528-
XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno);
530+
/* Also tell xlogutils.c about it */
531+
XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno);
532+
}
529533

530534
/* Truncate FSM and VM too */
531535
rel = CreateFakeRelcacheEntry(xlrec->rnode);
532536

533-
if (smgrexists(reln, FSM_FORKNUM))
537+
if ((xlrec->flags & SMGR_TRUNCATE_FSM) != 0 &&
538+
smgrexists(reln, FSM_FORKNUM))
534539
FreeSpaceMapTruncateRel(rel, xlrec->blkno);
535-
if (smgrexists(reln, VISIBILITYMAP_FORKNUM))
540+
if ((xlrec->flags & SMGR_TRUNCATE_VM) != 0 &&
541+
smgrexists(reln, VISIBILITYMAP_FORKNUM))
536542
visibilitymap_truncate(rel, xlrec->blkno);
537543

538544
FreeFakeRelcacheEntry(rel);

src/include/access/xlog_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
/*
3232
* Each page of XLOG file has a header like this:
3333
*/
34-
#define XLOG_PAGE_MAGIC 0xD091 /* can be used as WAL version indicator */
34+
#define XLOG_PAGE_MAGIC 0xD092 /* can be used as WAL version indicator */
3535

3636
typedef struct XLogPageHeaderData
3737
{

src/include/catalog/storage_xlog.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,18 @@ typedef struct xl_smgr_create
3636
ForkNumber forkNum;
3737
} xl_smgr_create;
3838

39+
/* flags for xl_smgr_truncate */
40+
#define SMGR_TRUNCATE_HEAP 0x0001
41+
#define SMGR_TRUNCATE_VM 0x0002
42+
#define SMGR_TRUNCATE_FSM 0x0004
43+
#define SMGR_TRUNCATE_ALL \
44+
(SMGR_TRUNCATE_HEAP|SMGR_TRUNCATE_VM|SMGR_TRUNCATE_FSM)
45+
3946
typedef struct xl_smgr_truncate
4047
{
4148
BlockNumber blkno;
4249
RelFileNode rnode;
50+
int flags;
4351
} xl_smgr_truncate;
4452

4553
extern void log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy