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
2022PG_MODULE_MAGIC ;
@@ -40,6 +42,7 @@ PG_FUNCTION_INFO_V1(pg_visibility_rel);
4042PG_FUNCTION_INFO_V1 (pg_visibility_map_summary );
4143PG_FUNCTION_INFO_V1 (pg_check_frozen );
4244PG_FUNCTION_INFO_V1 (pg_check_visible );
45+ PG_FUNCTION_INFO_V1 (pg_truncate_visibility_map );
4346
4447static TupleDesc pg_visibility_tupdesc (bool include_blkno , bool include_pd );
4548static 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.
0 commit comments