]> granicus.if.org Git - postgresql/commitdiff
Remove superuser checks in pgstattuple
authorStephen Frost <sfrost@snowman.net>
Fri, 30 Sep 2016 02:13:38 +0000 (22:13 -0400)
committerStephen Frost <sfrost@snowman.net>
Fri, 30 Sep 2016 02:13:38 +0000 (22:13 -0400)
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 and rely on the
GRANT system to control access to those functions.

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).

Thanks to Tom and Andres for adding support for extensions to follow
update paths (see: 40b449a), allowing this patch to be much smaller
since no new base version script needed to be included.

Approach suggested by Noah.

Reviewed by Michael Paquier.

contrib/pgstattuple/Makefile
contrib/pgstattuple/pgstatapprox.c
contrib/pgstattuple/pgstatindex.c
contrib/pgstattuple/pgstattuple--1.4--1.5.sql [new file with mode: 0644]
contrib/pgstattuple/pgstattuple.c
contrib/pgstattuple/pgstattuple.control
doc/src/sgml/pgstattuple.sgml

index e732680deab8280992683ffcacd79af84e494849..294077d4c11e56fc6d097dd20be2c55a0a01a5e5 100644 (file)
@@ -4,9 +4,10 @@ MODULE_big     = pgstattuple
 OBJS           = pgstattuple.o pgstatindex.o pgstatapprox.o $(WIN32RES)
 
 EXTENSION = pgstattuple
-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
+DATA = pgstattuple--1.4.sql pgstattuple--1.4--1.5.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
index a49ff543d2daf3edb397b3ccfea886c86deb1055..f524fc4e3068996fc9c302934fe5e269c1dc65f5 100644 (file)
@@ -29,6 +29,9 @@
 #include "commands/vacuum.h"
 
 PG_FUNCTION_INFO_V1(pgstattuple_approx);
+PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
+
+Datum pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo);
 
 typedef struct output_type
 {
@@ -204,11 +207,42 @@ statapprox_heap(Relation rel, output_type *stat)
 
 /*
  * Returns estimated live/dead tuple statistics for the given relid.
+ *
+ * The superuser() check here must be kept as the library might be upgraded
+ * without the extension being upgraded, meaning that in pre-1.5 installations
+ * these functions could be called by any user.
  */
 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.5, we no longer need to check if the user
+ * is a superuser because we REVOKE EXECUTE on the SQL function from PUBLIC.
+ * Users can then grant access to it based on their policies.
+ *
+ * Otherwise identical to pgstattuple_approx (above).
+ */
+Datum
+pgstattuple_approx_v1_5(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 +251,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");
 
index 6084589e07dbdac03e06c1ae2a3baa7c60329ec9..d9a722ac6bb1fc9c1fd2fe0edb8330d0b43fce03 100644 (file)
@@ -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_5);
+PG_FUNCTION_INFO_V1(pgstatindexbyid_v1_5);
+PG_FUNCTION_INFO_V1(pg_relpages_v1_5);
+PG_FUNCTION_INFO_V1(pg_relpagesbyid_v1_5);
+PG_FUNCTION_INFO_V1(pgstatginindex_v1_5);
+
+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)
@@ -99,6 +107,10 @@ static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo);
  * pgstatindex()
  *
  * Usage: SELECT * FROM pgstatindex('t1_pkey');
+ *
+ * The superuser() check here must be kept as the library might be upgraded
+ * without the extension being upgraded, meaning that in pre-1.5 installations
+ * these functions could be called by any user.
  * ------------------------------------------------------
  */
 Datum
@@ -119,6 +131,31 @@ pgstatindex(PG_FUNCTION_ARGS)
        PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
 }
 
+/*
+ * As of pgstattuple version 1.5, 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_5(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));
+}
+
+/*
+ * The superuser() check here must be kept as the library might be upgraded
+ * without the extension being upgraded, meaning that in pre-1.5 installations
+ * these functions could be called by any user.
+ */
 Datum
 pgstatindexbyid(PG_FUNCTION_ARGS)
 {
@@ -135,6 +172,18 @@ pgstatindexbyid(PG_FUNCTION_ARGS)
        PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
 }
 
+/* No need for superuser checks in v1.5, see above */
+Datum
+pgstatindexbyid_v1_5(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)
 {
@@ -292,6 +341,8 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
  *
  * Usage: SELECT pg_relpages('t1');
  *               SELECT pg_relpages('t1_pkey');
+ *
+ * Must keep superuser() check, see above.
  * --------------------------------------------------------
  */
 Datum
@@ -319,6 +370,28 @@ pg_relpages(PG_FUNCTION_ARGS)
        PG_RETURN_INT64(relpages);
 }
 
+/* No need for superuser checks in v1.5, see above */
+Datum
+pg_relpages_v1_5(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);
+}
+
+/* Must keep superuser() check, see above. */
 Datum
 pg_relpagesbyid(PG_FUNCTION_ARGS)
 {
@@ -342,16 +415,58 @@ pg_relpagesbyid(PG_FUNCTION_ARGS)
        PG_RETURN_INT64(relpages);
 }
 
+/* No need for superuser checks in v1.5, see above */
+Datum
+pg_relpagesbyid_v1_5(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()
  *
  * Usage: SELECT * FROM pgstatginindex('ginindex');
+ *
+ * Must keep superuser() check, see above.
  * ------------------------------------------------------
  */
 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));
+}
+
+/* No need for superuser checks in v1.5, see above */
+Datum
+pgstatginindex_v1_5(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;
@@ -363,11 +478,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))
@@ -415,5 +525,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.4--1.5.sql b/contrib/pgstattuple/pgstattuple--1.4--1.5.sql
new file mode 100644 (file)
index 0000000..65d7f19
--- /dev/null
@@ -0,0 +1,111 @@
+/* contrib/pgstattuple/pgstattuple--1.4--1.5.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pgstattuple UPDATE TO '1.5'" 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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+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_5'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+REVOKE EXECUTE ON FUNCTION pgstattuple_approx(regclass) FROM PUBLIC;
index c1122b496ad539a3281f8c432e548fa24401bf6e..68b07aaf267edb0fde8b976eab0a66de68080047 100644 (file)
@@ -40,7 +40,9 @@
 PG_MODULE_MAGIC;
 
 PG_FUNCTION_INFO_V1(pgstattuple);
+PG_FUNCTION_INFO_V1(pgstattuple_v1_5);
 PG_FUNCTION_INFO_V1(pgstattuplebyid);
+PG_FUNCTION_INFO_V1(pgstattuplebyid_v1_5);
 
 /*
  * struct pgstattuple_type
@@ -152,6 +154,10 @@ build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo)
  *
  * C FUNCTION definition
  * pgstattuple(text) returns pgstattuple_type
+ *
+ * The superuser() check here must be kept as the library might be upgraded
+ * without the extension being upgraded, meaning that in pre-1.5 installations
+ * these functions could be called by any user.
  * ----------
  */
 
@@ -174,6 +180,28 @@ pgstattuple(PG_FUNCTION_ARGS)
        PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
 }
 
+/*
+ * As of pgstattuple version 1.5, 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_5(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));
+}
+
+/* Must keep superuser() check, see above. */
 Datum
 pgstattuplebyid(PG_FUNCTION_ARGS)
 {
@@ -191,6 +219,19 @@ pgstattuplebyid(PG_FUNCTION_ARGS)
        PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
 }
 
+/* Remove superuser() check for 1.5 version, see above */
+Datum
+pgstattuplebyid_v1_5(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
  */
index fa328fd6648ac278a9f29bba2665cf8a8a314cd9..6af40757b27c2ff35b625dfa506bd34a9e7f7934 100644 (file)
@@ -1,5 +1,5 @@
 # pgstattuple extension
 comment = 'show tuple-level statistics'
-default_version = '1.4'
+default_version = '1.5'
 module_pathname = '$libdir/pgstattuple'
 relocatable = true
index 61340bedbc37866be92abc5558c4a7a7150dbb45..9ada5d209afcbab8b2c1edab6119033211ce947f 100644 (file)
   obtain tuple-level statistics.
  </para>
 
+ <para>
+  As these functions return detailed page-level information, only the superuser
+  has EXECUTE privileges on them upon installation.  After the functions have
+  been installed, users may issue <command>GRANT</command> commands to change
+  the privileges on the functions to allow non-superusers to execute them.  See
+  the description of the <xref linkend="sql-grant"> command for specifics.
+ </para>
+
  <sect2>
   <title>Functions</title>