diff --git a/contrib/pg_freespacemap/pg_freespacemap--1.0.sql b/contrib/pg_freespacemap/pg_freespacemap--1.0.sql
index 2adb52a..eb3e752 100644
--- a/contrib/pg_freespacemap/pg_freespacemap--1.0.sql
+++ b/contrib/pg_freespacemap/pg_freespacemap--1.0.sql
@@ -9,6 +9,16 @@ RETURNS int2
 AS 'MODULE_PATHNAME', 'pg_freespace'
 LANGUAGE C STRICT;
 
+CREATE FUNCTION pg_is_all_visible(regclass, bigint)
+RETURNS bool
+AS 'MODULE_PATHNAME', 'pg_is_all_visible'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION pg_is_all_frozen(regclass, bigint)
+RETURNS bool
+AS 'MODULE_PATHNAME', 'pg_is_all_frozen'
+LANGUAGE C STRICT;
+
 -- pg_freespace shows the recorded space avail at each block in a relation
 CREATE FUNCTION
   pg_freespace(rel regclass, blkno OUT bigint, avail OUT int2)
@@ -19,7 +29,18 @@ AS $$
 $$
 LANGUAGE SQL;
 
+CREATE FUNCTION
+  pg_visibilitymap(rel regclass, blkno OUT bigint, all_visible OUT bool, all_frozen OUT bool)
+RETURNS SETOF RECORD
+AS $$
+  SELECT blkno, pg_is_all_visible($1, blkno) AS all_visible, pg_is_all_frozen($1, blkno) AS all_frozen
+  FROM generate_series(0, pg_relation_size($1) / current_setting('block_size')::bigint - 1) AS blkno;
+$$
+LANGUAGE SQL;
 
 -- Don't want these to be available to public.
 REVOKE ALL ON FUNCTION pg_freespace(regclass, bigint) FROM PUBLIC;
 REVOKE ALL ON FUNCTION pg_freespace(regclass) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_is_all_visible(regclass, bigint) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_is_all_frozen(regclass, bigint) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_visibilitymap(rel regclass) FROM PUBLIC;
diff --git a/contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql b/contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql
index 8651373..ebd5f5f 100644
--- a/contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql
+++ b/contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql
@@ -5,3 +5,6 @@
 
 ALTER EXTENSION pg_freespacemap ADD function pg_freespace(regclass,bigint);
 ALTER EXTENSION pg_freespacemap ADD function pg_freespace(regclass);
+ALTER EXTENSION pg_freespacemap ADD function pg_is_all_visible(regclass, bigint);
+ALTER EXTENSION pg_freespacemap ADD function pg_is_all_frozen(regclass, bigint);
+ALTER EXTENSION pg_freespacemap ADD function pg_visibilitymap(regclass);
diff --git a/contrib/pg_freespacemap/pg_freespacemap.c b/contrib/pg_freespacemap/pg_freespacemap.c
index 7d939a7..719c879 100644
--- a/contrib/pg_freespacemap/pg_freespacemap.c
+++ b/contrib/pg_freespacemap/pg_freespacemap.c
@@ -8,8 +8,10 @@
  */
 #include "postgres.h"
 
+#include "access/visibilitymap.h"
 #include "funcapi.h"
 #include "storage/freespace.h"
+#include "storage/bufmgr.h"
 
 PG_MODULE_MAGIC;
 
@@ -18,6 +20,10 @@ PG_MODULE_MAGIC;
  * free space map.
  */
 PG_FUNCTION_INFO_V1(pg_freespace);
+PG_FUNCTION_INFO_V1(pg_is_all_visible);
+PG_FUNCTION_INFO_V1(pg_is_all_frozen);
+
+static bool visibilitymap_test_internal(Oid relid, uint64 blkno, uint8);
 
 Datum
 pg_freespace(PG_FUNCTION_ARGS)
@@ -39,3 +45,56 @@ pg_freespace(PG_FUNCTION_ARGS)
 	relation_close(rel, AccessShareLock);
 	PG_RETURN_INT16(freespace);
 }
+
+/*
+ * Return the page is all-visible or not, according to the visibility map.
+ */
+Datum
+pg_is_all_visible(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		blkno = PG_GETARG_INT64(1);
+	bool		all_visible;
+
+	all_visible = visibilitymap_test_internal(relid, blkno, VISIBILITYMAP_ALL_VISIBLE);
+
+	PG_RETURN_BOOL(all_visible);
+}
+
+/*
+ * Return the page is all-frozen or not, according to the visibility map.
+ */
+Datum
+pg_is_all_frozen(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		blkno = PG_GETARG_INT64(1);
+	bool		all_frozen;
+
+	all_frozen = visibilitymap_test_internal(relid, blkno, VISIBILITYMAP_ALL_FROZEN);
+
+	PG_RETURN_BOOL(all_frozen);
+}
+
+static bool
+visibilitymap_test_internal(Oid relid, uint64 blkno, uint8 flag)
+{
+
+	Relation	rel;
+	Buffer		vmbuffer = InvalidBuffer;
+	bool		result;
+
+	rel = relation_open(relid, AccessShareLock);
+
+	if (blkno < 0 || blkno > MaxBlockNumber)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid block number")));
+
+	result = visibilitymap_test(rel, blkno, &vmbuffer, flag);
+
+	ReleaseBuffer(vmbuffer);
+	relation_close(rel, AccessShareLock);
+
+	return result;
+}
