Noah, Fujii, all,

* Noah Misch (n...@leadboat.com) wrote:
> At the C level, have a pgstattuple function and a pgstattuple_v1_4 function.
> Let them differ only in that the former has a superuser check.  Binary
> upgrades will use the former, and fresh CREATE EXTENSION shall use the latter.

Attached is a patch which implements this for the pgstattuple
extensions.  The changes are pretty straight-forward, but I'm not going
to commit this under the gun of the feature freeze without at least
another committer reviewing it or getting an extension for a couple days
to play with it further and convince myself it's safe.

Ultimately, I'd like for this to be included in 9.6 as it'd be an
example use-case for others to follow when updating their extensions to
make use of the new pg_dump features, but I certainly don't see it as
being critical to the release.

Fujii, my apologies for not getting this done earlier, I know this is a
capability you are looking forward to having.

Thanks!

Stephen
From 0db7ebf549aeee7f04b8383ac391f349f810ef4b Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfr...@snowman.net>
Date: Fri, 8 Apr 2016 17:18:27 -0400
Subject: [PATCH] Remove superuser checks in pgstattuple 1.4

Now that we track initial privileges on extension objects and changes to
those permissions, we can drop the superuser() checks from the various
functions which are part of the pgstattuple extension.

Since a pg_upgrade will preserve the version of the extension which
existed prior to the upgrade, we can't simply modify the existing
functions but instead need to create new functions which remove the
checks and update the SQL-level functions to use the new functions
(and to REVOKE EXECUTE rights on those functions from PUBLIC).

Approach suggested by Noah.
---
 contrib/pgstattuple/Makefile                  |   2 +-
 contrib/pgstattuple/pgstatapprox.c            |  35 ++++++--
 contrib/pgstattuple/pgstatindex.c             | 108 +++++++++++++++++++++++--
 contrib/pgstattuple/pgstattuple--1.3--1.4.sql | 111 ++++++++++++++++++++++++++
 contrib/pgstattuple/pgstattuple--1.3.sql      |  95 ----------------------
 contrib/pgstattuple/pgstattuple--1.4.sql      | 111 ++++++++++++++++++++++++++
 contrib/pgstattuple/pgstattuple.c             |  36 +++++++++
 contrib/pgstattuple/pgstattuple.control       |   2 +-
 8 files changed, 392 insertions(+), 108 deletions(-)
 create mode 100644 contrib/pgstattuple/pgstattuple--1.3--1.4.sql
 delete mode 100644 contrib/pgstattuple/pgstattuple--1.3.sql
 create mode 100644 contrib/pgstattuple/pgstattuple--1.4.sql

diff --git a/contrib/pgstattuple/Makefile b/contrib/pgstattuple/Makefile
index 6083dab..01f1feb 100644
--- a/contrib/pgstattuple/Makefile
+++ b/contrib/pgstattuple/Makefile
@@ -4,7 +4,7 @@ MODULE_big	= pgstattuple
 OBJS		= pgstattuple.o pgstatindex.o pgstatapprox.o $(WIN32RES)
 
 EXTENSION = pgstattuple
-DATA = pgstattuple--1.3.sql pgstattuple--1.2--1.3.sql pgstattuple--1.1--1.2.sql pgstattuple--1.0--1.1.sql pgstattuple--unpackaged--1.0.sql
+DATA = pgstattuple--1.4.sql pgstattuple--1.3--1.4.sql pgstattuple--1.2--1.3.sql pgstattuple--1.1--1.2.sql pgstattuple--1.0--1.1.sql pgstattuple--unpackaged--1.0.sql
 PGFILEDESC = "pgstattuple - tuple-level statistics"
 
 REGRESS = pgstattuple
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index b7734fa..5671791 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -29,6 +29,9 @@
 #include "commands/vacuum.h"
 
 PG_FUNCTION_INFO_V1(pgstattuple_approx);
+PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_4);
+
+Datum pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo);
 
 typedef struct output_type
 {
@@ -209,6 +212,33 @@ Datum
 pgstattuple_approx(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pgstattuple functions"))));
+
+	PG_RETURN_DATUM(pgstattuple_approx_internal(relid, fcinfo));
+}
+
+/*
+ * As of pgstattuple version 1.4, we no longer need to check if the user
+ * is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
+ * Users can then grant access to it based on their policies.
+ *
+ * Otherwise identical to pgstattuple_approx (above).
+ */
+Datum
+pgstattuple_approx_v1_4(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+
+	PG_RETURN_DATUM(pgstattuple_approx_internal(relid, fcinfo));
+}
+
+Datum
+pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo)
+{
 	Relation	rel;
 	output_type stat = {0};
 	TupleDesc	tupdesc;
@@ -217,11 +247,6 @@ pgstattuple_approx(PG_FUNCTION_ARGS)
 	HeapTuple	ret;
 	int			i = 0;
 
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to use pgstattuple functions"))));
-
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index 4596632..d03be25 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -54,6 +54,14 @@ PG_FUNCTION_INFO_V1(pg_relpages);
 PG_FUNCTION_INFO_V1(pg_relpagesbyid);
 PG_FUNCTION_INFO_V1(pgstatginindex);
 
+PG_FUNCTION_INFO_V1(pgstatindex_v1_4);
+PG_FUNCTION_INFO_V1(pgstatindexbyid_v1_4);
+PG_FUNCTION_INFO_V1(pg_relpages_v1_4);
+PG_FUNCTION_INFO_V1(pg_relpagesbyid_v1_4);
+PG_FUNCTION_INFO_V1(pgstatginindex_v1_4);
+
+Datum pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo);
+
 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
 #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
 #define IS_GIN(r) ((r)->rd_rel->relam == GIN_AM_OID)
@@ -129,6 +137,26 @@ pgstatindex(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
 }
 
+/*
+ * As of pgstattuple version 1.4, we no longer need to check if the user
+ * is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
+ * Users can then grant access to it based on their policies.
+ *
+ * Otherwise identical to pgstatindex (above).
+ */
+Datum
+pgstatindex_v1_4(PG_FUNCTION_ARGS)
+{
+	text	   *relname = PG_GETARG_TEXT_P(0);
+	Relation	rel;
+	RangeVar   *relrv;
+
+	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+	rel = relation_openrv(relrv, AccessShareLock);
+
+	PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
+}
+
 Datum
 pgstatindexbyid(PG_FUNCTION_ARGS)
 {
@@ -145,6 +173,18 @@ pgstatindexbyid(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
 }
 
+/* Remove superuser checks in v1.4, see above */
+Datum
+pgstatindexbyid_v1_4(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Relation	rel;
+
+	rel = relation_open(relid, AccessShareLock);
+
+	PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
+}
+
 static Datum
 pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 {
@@ -329,6 +369,27 @@ pg_relpages(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(relpages);
 }
 
+/* Remove superuser checks in v1.4, see above */
+Datum
+pg_relpages_v1_4(PG_FUNCTION_ARGS)
+{
+	text	   *relname = PG_GETARG_TEXT_P(0);
+	int64		relpages;
+	Relation	rel;
+	RangeVar   *relrv;
+
+	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+	rel = relation_openrv(relrv, AccessShareLock);
+
+	/* note: this will work OK on non-local temp tables */
+
+	relpages = RelationGetNumberOfBlocks(rel);
+
+	relation_close(rel, AccessShareLock);
+
+	PG_RETURN_INT64(relpages);
+}
+
 Datum
 pg_relpagesbyid(PG_FUNCTION_ARGS)
 {
@@ -352,6 +413,25 @@ pg_relpagesbyid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(relpages);
 }
 
+/* Remove superuser checks in v1.4, see above */
+Datum
+pg_relpagesbyid_v1_4(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		relpages;
+	Relation	rel;
+
+	rel = relation_open(relid, AccessShareLock);
+
+	/* note: this will work OK on non-local temp tables */
+
+	relpages = RelationGetNumberOfBlocks(rel);
+
+	relation_close(rel, AccessShareLock);
+
+	PG_RETURN_INT64(relpages);
+}
+
 /* ------------------------------------------------------
  * pgstatginindex()
  *
@@ -362,6 +442,27 @@ Datum
 pgstatginindex(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pgstattuple functions"))));
+
+	PG_RETURN_DATUM(pgstatginindex_internal(relid, fcinfo));
+}
+
+/* Remove superuser checks in v1.4, see above */
+Datum
+pgstatginindex_v1_4(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+
+	PG_RETURN_DATUM(pgstatginindex_internal(relid, fcinfo));
+}
+
+Datum
+pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
+{
 	Relation	rel;
 	Buffer		buffer;
 	Page		page;
@@ -373,11 +474,6 @@ pgstatginindex(PG_FUNCTION_ARGS)
 	bool		nulls[3] = {false, false, false};
 	Datum		result;
 
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to use pgstattuple functions"))));
-
 	rel = relation_open(relid, AccessShareLock);
 
 	if (!IS_INDEX(rel) || !IS_GIN(rel))
@@ -425,5 +521,5 @@ pgstatginindex(PG_FUNCTION_ARGS)
 	tuple = heap_form_tuple(tupleDesc, values, nulls);
 	result = HeapTupleGetDatum(tuple);
 
-	PG_RETURN_DATUM(result);
+	return (result);
 }
diff --git a/contrib/pgstattuple/pgstattuple--1.3--1.4.sql b/contrib/pgstattuple/pgstattuple--1.3--1.4.sql
new file mode 100644
index 0000000..80f18c3
--- /dev/null
+++ b/contrib/pgstattuple/pgstattuple--1.3--1.4.sql
@@ -0,0 +1,111 @@
+/* contrib/pgstattuple/pgstattuple--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pgstattuple" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION pgstattuple(IN relname text,
+    OUT table_len BIGINT,		-- physical table length in bytes
+    OUT tuple_count BIGINT,		-- number of live tuples
+    OUT tuple_len BIGINT,		-- total tuples length in bytes
+    OUT tuple_percent FLOAT8,		-- live tuples in %
+    OUT dead_tuple_count BIGINT,	-- number of dead tuples
+    OUT dead_tuple_len BIGINT,		-- total dead tuples length in bytes
+    OUT dead_tuple_percent FLOAT8,	-- dead tuples in %
+    OUT free_space BIGINT,		-- free space in bytes
+    OUT free_percent FLOAT8)		-- free space in %
+AS 'MODULE_PATHNAME', 'pgstattuple_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstattuple(text) FROM PUBLIC;
+
+CREATE OR REPLACE FUNCTION pgstatindex(IN relname text,
+    OUT version INT,
+    OUT tree_level INT,
+    OUT index_size BIGINT,
+    OUT root_block_no BIGINT,
+    OUT internal_pages BIGINT,
+    OUT leaf_pages BIGINT,
+    OUT empty_pages BIGINT,
+    OUT deleted_pages BIGINT,
+    OUT avg_leaf_density FLOAT8,
+    OUT leaf_fragmentation FLOAT8)
+AS 'MODULE_PATHNAME', 'pgstatindex_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstatindex(text) FROM PUBLIC;
+
+CREATE OR REPLACE FUNCTION pg_relpages(IN relname text)
+RETURNS BIGINT
+AS 'MODULE_PATHNAME', 'pg_relpages_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pg_relpages(text) FROM PUBLIC;
+
+/* New stuff in 1.1 begins here */
+
+CREATE OR REPLACE FUNCTION pgstatginindex(IN relname regclass,
+    OUT version INT4,
+    OUT pending_pages INT4,
+    OUT pending_tuples BIGINT)
+AS 'MODULE_PATHNAME', 'pgstatginindex_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstatginindex(regclass) FROM PUBLIC;
+
+/* New stuff in 1.2 begins here */
+
+CREATE OR REPLACE FUNCTION pgstattuple(IN reloid regclass,
+    OUT table_len BIGINT,		-- physical table length in bytes
+    OUT tuple_count BIGINT,		-- number of live tuples
+    OUT tuple_len BIGINT,		-- total tuples length in bytes
+    OUT tuple_percent FLOAT8,		-- live tuples in %
+    OUT dead_tuple_count BIGINT,	-- number of dead tuples
+    OUT dead_tuple_len BIGINT,		-- total dead tuples length in bytes
+    OUT dead_tuple_percent FLOAT8,	-- dead tuples in %
+    OUT free_space BIGINT,		-- free space in bytes
+    OUT free_percent FLOAT8)		-- free space in %
+AS 'MODULE_PATHNAME', 'pgstattuplebyid_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstattuple(regclass) FROM PUBLIC;
+
+CREATE OR REPLACE FUNCTION pgstatindex(IN relname regclass,
+    OUT version INT,
+    OUT tree_level INT,
+    OUT index_size BIGINT,
+    OUT root_block_no BIGINT,
+    OUT internal_pages BIGINT,
+    OUT leaf_pages BIGINT,
+    OUT empty_pages BIGINT,
+    OUT deleted_pages BIGINT,
+    OUT avg_leaf_density FLOAT8,
+    OUT leaf_fragmentation FLOAT8)
+AS 'MODULE_PATHNAME', 'pgstatindexbyid_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstatindex(regclass) FROM PUBLIC;
+
+CREATE OR REPLACE FUNCTION pg_relpages(IN relname regclass)
+RETURNS BIGINT
+AS 'MODULE_PATHNAME', 'pg_relpagesbyid_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pg_relpages(regclass) FROM PUBLIC;
+
+/* New stuff in 1.3 begins here */
+
+CREATE OR REPLACE FUNCTION pgstattuple_approx(IN reloid regclass,
+    OUT table_len BIGINT,               -- physical table length in bytes
+    OUT scanned_percent FLOAT8,         -- what percentage of the table's pages was scanned
+    OUT approx_tuple_count BIGINT,      -- estimated number of live tuples
+    OUT approx_tuple_len BIGINT,        -- estimated total length in bytes of live tuples
+    OUT approx_tuple_percent FLOAT8,    -- live tuples in % (based on estimate)
+    OUT dead_tuple_count BIGINT,        -- exact number of dead tuples
+    OUT dead_tuple_len BIGINT,          -- exact total length in bytes of dead tuples
+    OUT dead_tuple_percent FLOAT8,      -- dead tuples in % (based on estimate)
+    OUT approx_free_space BIGINT,       -- estimated free space in bytes
+    OUT approx_free_percent FLOAT8)     -- free space in % (based on estimate)
+AS 'MODULE_PATHNAME', 'pgstattuple_approx_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstattuple_approx(regclass) FROM PUBLIC;
diff --git a/contrib/pgstattuple/pgstattuple--1.3.sql b/contrib/pgstattuple/pgstattuple--1.3.sql
deleted file mode 100644
index f3996e7..0000000
--- a/contrib/pgstattuple/pgstattuple--1.3.sql
+++ /dev/null
@@ -1,95 +0,0 @@
-/* contrib/pgstattuple/pgstattuple--1.3.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION pgstattuple" to load this file. \quit
-
-CREATE FUNCTION pgstattuple(IN relname text,
-    OUT table_len BIGINT,		-- physical table length in bytes
-    OUT tuple_count BIGINT,		-- number of live tuples
-    OUT tuple_len BIGINT,		-- total tuples length in bytes
-    OUT tuple_percent FLOAT8,		-- live tuples in %
-    OUT dead_tuple_count BIGINT,	-- number of dead tuples
-    OUT dead_tuple_len BIGINT,		-- total dead tuples length in bytes
-    OUT dead_tuple_percent FLOAT8,	-- dead tuples in %
-    OUT free_space BIGINT,		-- free space in bytes
-    OUT free_percent FLOAT8)		-- free space in %
-AS 'MODULE_PATHNAME', 'pgstattuple'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION pgstatindex(IN relname text,
-    OUT version INT,
-    OUT tree_level INT,
-    OUT index_size BIGINT,
-    OUT root_block_no BIGINT,
-    OUT internal_pages BIGINT,
-    OUT leaf_pages BIGINT,
-    OUT empty_pages BIGINT,
-    OUT deleted_pages BIGINT,
-    OUT avg_leaf_density FLOAT8,
-    OUT leaf_fragmentation FLOAT8)
-AS 'MODULE_PATHNAME', 'pgstatindex'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION pg_relpages(IN relname text)
-RETURNS BIGINT
-AS 'MODULE_PATHNAME', 'pg_relpages'
-LANGUAGE C STRICT;
-
-/* New stuff in 1.1 begins here */
-
-CREATE FUNCTION pgstatginindex(IN relname regclass,
-    OUT version INT4,
-    OUT pending_pages INT4,
-    OUT pending_tuples BIGINT)
-AS 'MODULE_PATHNAME', 'pgstatginindex'
-LANGUAGE C STRICT;
-
-/* New stuff in 1.2 begins here */
-
-CREATE FUNCTION pgstattuple(IN reloid regclass,
-    OUT table_len BIGINT,		-- physical table length in bytes
-    OUT tuple_count BIGINT,		-- number of live tuples
-    OUT tuple_len BIGINT,		-- total tuples length in bytes
-    OUT tuple_percent FLOAT8,		-- live tuples in %
-    OUT dead_tuple_count BIGINT,	-- number of dead tuples
-    OUT dead_tuple_len BIGINT,		-- total dead tuples length in bytes
-    OUT dead_tuple_percent FLOAT8,	-- dead tuples in %
-    OUT free_space BIGINT,		-- free space in bytes
-    OUT free_percent FLOAT8)		-- free space in %
-AS 'MODULE_PATHNAME', 'pgstattuplebyid'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION pgstatindex(IN relname regclass,
-    OUT version INT,
-    OUT tree_level INT,
-    OUT index_size BIGINT,
-    OUT root_block_no BIGINT,
-    OUT internal_pages BIGINT,
-    OUT leaf_pages BIGINT,
-    OUT empty_pages BIGINT,
-    OUT deleted_pages BIGINT,
-    OUT avg_leaf_density FLOAT8,
-    OUT leaf_fragmentation FLOAT8)
-AS 'MODULE_PATHNAME', 'pgstatindexbyid'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION pg_relpages(IN relname regclass)
-RETURNS BIGINT
-AS 'MODULE_PATHNAME', 'pg_relpagesbyid'
-LANGUAGE C STRICT;
-
-/* New stuff in 1.3 begins here */
-
-CREATE FUNCTION pgstattuple_approx(IN reloid regclass,
-    OUT table_len BIGINT,               -- physical table length in bytes
-    OUT scanned_percent FLOAT8,         -- what percentage of the table's pages was scanned
-    OUT approx_tuple_count BIGINT,      -- estimated number of live tuples
-    OUT approx_tuple_len BIGINT,        -- estimated total length in bytes of live tuples
-    OUT approx_tuple_percent FLOAT8,    -- live tuples in % (based on estimate)
-    OUT dead_tuple_count BIGINT,        -- exact number of dead tuples
-    OUT dead_tuple_len BIGINT,          -- exact total length in bytes of dead tuples
-    OUT dead_tuple_percent FLOAT8,      -- dead tuples in % (based on estimate)
-    OUT approx_free_space BIGINT,       -- estimated free space in bytes
-    OUT approx_free_percent FLOAT8)     -- free space in % (based on estimate)
-AS 'MODULE_PATHNAME', 'pgstattuple_approx'
-LANGUAGE C STRICT;
diff --git a/contrib/pgstattuple/pgstattuple--1.4.sql b/contrib/pgstattuple/pgstattuple--1.4.sql
new file mode 100644
index 0000000..be7f003
--- /dev/null
+++ b/contrib/pgstattuple/pgstattuple--1.4.sql
@@ -0,0 +1,111 @@
+/* contrib/pgstattuple/pgstattuple--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pgstattuple" to load this file. \quit
+
+CREATE FUNCTION pgstattuple(IN relname text,
+    OUT table_len BIGINT,		-- physical table length in bytes
+    OUT tuple_count BIGINT,		-- number of live tuples
+    OUT tuple_len BIGINT,		-- total tuples length in bytes
+    OUT tuple_percent FLOAT8,		-- live tuples in %
+    OUT dead_tuple_count BIGINT,	-- number of dead tuples
+    OUT dead_tuple_len BIGINT,		-- total dead tuples length in bytes
+    OUT dead_tuple_percent FLOAT8,	-- dead tuples in %
+    OUT free_space BIGINT,		-- free space in bytes
+    OUT free_percent FLOAT8)		-- free space in %
+AS 'MODULE_PATHNAME', 'pgstattuple_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstattuple(text) FROM PUBLIC;
+
+CREATE FUNCTION pgstatindex(IN relname text,
+    OUT version INT,
+    OUT tree_level INT,
+    OUT index_size BIGINT,
+    OUT root_block_no BIGINT,
+    OUT internal_pages BIGINT,
+    OUT leaf_pages BIGINT,
+    OUT empty_pages BIGINT,
+    OUT deleted_pages BIGINT,
+    OUT avg_leaf_density FLOAT8,
+    OUT leaf_fragmentation FLOAT8)
+AS 'MODULE_PATHNAME', 'pgstatindex_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstatindex(text) FROM PUBLIC;
+
+CREATE FUNCTION pg_relpages(IN relname text)
+RETURNS BIGINT
+AS 'MODULE_PATHNAME', 'pg_relpages_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pg_relpages(text) FROM PUBLIC;
+
+/* New stuff in 1.1 begins here */
+
+CREATE FUNCTION pgstatginindex(IN relname regclass,
+    OUT version INT4,
+    OUT pending_pages INT4,
+    OUT pending_tuples BIGINT)
+AS 'MODULE_PATHNAME', 'pgstatginindex_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstatginindex(regclass) FROM PUBLIC;
+
+/* New stuff in 1.2 begins here */
+
+CREATE FUNCTION pgstattuple(IN reloid regclass,
+    OUT table_len BIGINT,		-- physical table length in bytes
+    OUT tuple_count BIGINT,		-- number of live tuples
+    OUT tuple_len BIGINT,		-- total tuples length in bytes
+    OUT tuple_percent FLOAT8,		-- live tuples in %
+    OUT dead_tuple_count BIGINT,	-- number of dead tuples
+    OUT dead_tuple_len BIGINT,		-- total dead tuples length in bytes
+    OUT dead_tuple_percent FLOAT8,	-- dead tuples in %
+    OUT free_space BIGINT,		-- free space in bytes
+    OUT free_percent FLOAT8)		-- free space in %
+AS 'MODULE_PATHNAME', 'pgstattuplebyid_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstattuple(regclass) FROM PUBLIC;
+
+CREATE FUNCTION pgstatindex(IN relname regclass,
+    OUT version INT,
+    OUT tree_level INT,
+    OUT index_size BIGINT,
+    OUT root_block_no BIGINT,
+    OUT internal_pages BIGINT,
+    OUT leaf_pages BIGINT,
+    OUT empty_pages BIGINT,
+    OUT deleted_pages BIGINT,
+    OUT avg_leaf_density FLOAT8,
+    OUT leaf_fragmentation FLOAT8)
+AS 'MODULE_PATHNAME', 'pgstatindexbyid_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstatindex(regclass) FROM PUBLIC;
+
+CREATE FUNCTION pg_relpages(IN relname regclass)
+RETURNS BIGINT
+AS 'MODULE_PATHNAME', 'pg_relpagesbyid_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pg_relpages(regclass) FROM PUBLIC;
+
+/* New stuff in 1.3 begins here */
+
+CREATE FUNCTION pgstattuple_approx(IN reloid regclass,
+    OUT table_len BIGINT,               -- physical table length in bytes
+    OUT scanned_percent FLOAT8,         -- what percentage of the table's pages was scanned
+    OUT approx_tuple_count BIGINT,      -- estimated number of live tuples
+    OUT approx_tuple_len BIGINT,        -- estimated total length in bytes of live tuples
+    OUT approx_tuple_percent FLOAT8,    -- live tuples in % (based on estimate)
+    OUT dead_tuple_count BIGINT,        -- exact number of dead tuples
+    OUT dead_tuple_len BIGINT,          -- exact total length in bytes of dead tuples
+    OUT dead_tuple_percent FLOAT8,      -- dead tuples in % (based on estimate)
+    OUT approx_free_space BIGINT,       -- estimated free space in bytes
+    OUT approx_free_percent FLOAT8)     -- free space in % (based on estimate)
+AS 'MODULE_PATHNAME', 'pgstattuple_approx_v1_4'
+LANGUAGE C STRICT;
+
+REVOKE EXECUTE ON FUNCTION pgstattuple_approx(regclass) FROM PUBLIC;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 46655ac..da0a8f1 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -40,7 +40,9 @@
 PG_MODULE_MAGIC;
 
 PG_FUNCTION_INFO_V1(pgstattuple);
+PG_FUNCTION_INFO_V1(pgstattuple_v1_4);
 PG_FUNCTION_INFO_V1(pgstattuplebyid);
+PG_FUNCTION_INFO_V1(pgstattuplebyid_v1_4);
 
 /*
  * struct pgstattuple_type
@@ -174,6 +176,27 @@ pgstattuple(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
 }
 
+/*
+ * As of pgstattuple version 1.4, we no longer need to check if the user
+ * is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
+ * Users can then grant access to it based on their policies.
+ *
+ * Otherwise identical to pgstattuple (above).
+ */
+Datum
+pgstattuple_v1_4(PG_FUNCTION_ARGS)
+{
+	text	   *relname = PG_GETARG_TEXT_P(0);
+	RangeVar   *relrv;
+	Relation	rel;
+
+	/* open relation */
+	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+	rel = relation_openrv(relrv, AccessShareLock);
+
+	PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
+}
+
 Datum
 pgstattuplebyid(PG_FUNCTION_ARGS)
 {
@@ -191,6 +214,19 @@ pgstattuplebyid(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
 }
 
+/* Remove superuser() check for 1.4 version, see above */
+Datum
+pgstattuplebyid_v1_4(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Relation	rel;
+
+	/* open relation */
+	rel = relation_open(relid, AccessShareLock);
+
+	PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
+}
+
 /*
  * pgstat_relation
  */
diff --git a/contrib/pgstattuple/pgstattuple.control b/contrib/pgstattuple/pgstattuple.control
index c03b180..fa328fd 100644
--- a/contrib/pgstattuple/pgstattuple.control
+++ b/contrib/pgstattuple/pgstattuple.control
@@ -1,5 +1,5 @@
 # pgstattuple extension
 comment = 'show tuple-level statistics'
-default_version = '1.3'
+default_version = '1.4'
 module_pathname = '$libdir/pgstattuple'
 relocatable = true
-- 
2.5.0

Attachment: signature.asc
Description: Digital signature

Reply via email to