11
11
#include "access/htup_details.h"
12
12
#include "access/visibilitymap.h"
13
13
#include "catalog/pg_type.h"
14
+ #include "catalog/storage_xlog.h"
14
15
#include "funcapi.h"
15
16
#include "miscadmin.h"
16
17
#include "storage/bufmgr.h"
17
18
#include "storage/procarray.h"
19
+ #include "storage/smgr.h"
18
20
#include "utils/rel.h"
19
21
20
22
PG_MODULE_MAGIC ;
@@ -40,6 +42,7 @@ PG_FUNCTION_INFO_V1(pg_visibility_rel);
40
42
PG_FUNCTION_INFO_V1 (pg_visibility_map_summary );
41
43
PG_FUNCTION_INFO_V1 (pg_check_frozen );
42
44
PG_FUNCTION_INFO_V1 (pg_check_visible );
45
+ PG_FUNCTION_INFO_V1 (pg_truncate_visibility_map );
43
46
44
47
static TupleDesc pg_visibility_tupdesc (bool include_blkno , bool include_pd );
45
48
static vbits * collect_visibility_data (Oid relid , bool include_pd );
@@ -335,6 +338,75 @@ pg_check_visible(PG_FUNCTION_ARGS)
335
338
SRF_RETURN_DONE (funcctx );
336
339
}
337
340
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
+
338
410
/*
339
411
* Helper function to construct whichever TupleDesc we need for a particular
340
412
* call.
0 commit comments