From 38c75ecf8345bd338112ad2a3221dfc47929fb7e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 26 Aug 2007 23:59:50 +0000
Subject: [PATCH] Restrict pgstattuple functions to superusers.  (This might be
 too strict, but no permissions check at all is certainly no good.)  Clean up
 usage of some deprecated APIs.

---
 contrib/pgstattuple/pgstatindex.c             | 46 +++++++------
 contrib/pgstattuple/pgstattuple.c             | 20 ++++--
 contrib/pgstattuple/pgstattuple.sql.in        | 66 +++++++++----------
 contrib/pgstattuple/uninstall_pgstattuple.sql |  6 +-
 4 files changed, 72 insertions(+), 66 deletions(-)

diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index 0c2c9a14484..3018b6aedd1 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -24,27 +24,21 @@
 
 #include "postgres.h"
 
-#include "fmgr.h"
-#include "funcapi.h"
 #include "access/heapam.h"
-#include "access/itup.h"
 #include "access/nbtree.h"
-#include "access/transam.h"
 #include "catalog/namespace.h"
-#include "catalog/pg_type.h"
+#include "funcapi.h"
+#include "miscadmin.h"
 #include "utils/builtins.h"
-#include "utils/inval.h"
 
-PG_FUNCTION_INFO_V1(pgstatindex);
-PG_FUNCTION_INFO_V1(pg_relpages);
 
 extern Datum pgstatindex(PG_FUNCTION_ARGS);
 extern Datum pg_relpages(PG_FUNCTION_ARGS);
 
-#define PGSTATINDEX_TYPE "public.pgstatindex_type"
-#define PGSTATINDEX_NCOLUMNS 10
+PG_FUNCTION_INFO_V1(pgstatindex);
+PG_FUNCTION_INFO_V1(pg_relpages);
 
-#define IS_INDEX(r) ((r)->rd_rel->relkind == 'i')
+#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
 #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
 
 #define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \
@@ -97,15 +91,20 @@ pgstatindex(PG_FUNCTION_ARGS)
 	uint32		blkno;
 	BTIndexStat indexStat;
 
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pgstattuple functions"))));
+
 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 	rel = relation_openrv(relrv, AccessShareLock);
 
 	if (!IS_INDEX(rel) || !IS_BTREE(rel))
-		elog(ERROR, "pgstatindex() can only be used on b-tree index");
+		elog(ERROR, "relation \"%s\" is not a btree index",
+			 RelationGetRelationName(rel));
 
-	/*-------------------
-	 * Read a metapage
-	 *-------------------
+	/*
+	 * Read metapage
 	 */
 	{
 		Buffer		buffer = ReadBuffer(rel, 0);
@@ -194,11 +193,12 @@ pgstatindex(PG_FUNCTION_ARGS)
 	{
 		TupleDesc	tupleDesc;
 		int			j;
-		char	   *values[PGSTATINDEX_NCOLUMNS];
-
+		char	   *values[10];
 		HeapTuple	tuple;
 
-		tupleDesc = RelationNameGetTupleDesc(PGSTATINDEX_TYPE);
+		/* Build a tuple descriptor for our result type */
+		if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+			elog(ERROR, "return type must be a row type");
 
 		j = 0;
 		values[j] = palloc(32);
@@ -229,7 +229,7 @@ pgstatindex(PG_FUNCTION_ARGS)
 		tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
 									   values);
 
-		result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple);
+		result = HeapTupleGetDatum(tuple);
 	}
 
 	PG_RETURN_DATUM(result);
@@ -238,7 +238,7 @@ pgstatindex(PG_FUNCTION_ARGS)
 /* --------------------------------------------------------
  * pg_relpages()
  *
- * Get a number of pages of the table/index.
+ * Get the number of pages of the table/index.
  *
  * Usage: SELECT pg_relpages('t1');
  *		  SELECT pg_relpages('t1_pkey');
@@ -248,11 +248,15 @@ Datum
 pg_relpages(PG_FUNCTION_ARGS)
 {
 	text	   *relname = PG_GETARG_TEXT_P(0);
-
 	Relation	rel;
 	RangeVar   *relrv;
 	int4		relpages;
 
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pgstattuple functions"))));
+
 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 	rel = relation_openrv(relrv, AccessShareLock);
 
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 64e7f982fba..9072a37aa91 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.27 2007/05/03 16:45:58 tgl Exp $
+ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.28 2007/08/26 23:59:50 tgl Exp $
  *
  * Copyright (c) 2001,2002	Tatsuo Ishii
  *
@@ -24,14 +24,13 @@
 
 #include "postgres.h"
 
-#include "fmgr.h"
-#include "funcapi.h"
 #include "access/gist_private.h"
 #include "access/hash.h"
 #include "access/heapam.h"
 #include "access/nbtree.h"
-#include "access/transam.h"
 #include "catalog/namespace.h"
+#include "funcapi.h"
+#include "miscadmin.h"
 #include "utils/builtins.h"
 
 
@@ -99,9 +98,6 @@ build_pgstattuple_type(pgstattuple_type * stat, FunctionCallInfo fcinfo)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	/* make sure we have a persistent copy of the tupdesc */
-	tupdesc = CreateTupleDescCopy(tupdesc);
-
 	/*
 	 * Generate attribute metadata needed later to produce tuples from raw C
 	 * strings
@@ -163,6 +159,11 @@ pgstattuple(PG_FUNCTION_ARGS)
 	RangeVar   *relrv;
 	Relation	rel;
 
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pgstattuple functions"))));
+
 	/* open relation */
 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 	rel = relation_openrv(relrv, AccessShareLock);
@@ -176,6 +177,11 @@ pgstattuplebyid(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	Relation	rel;
 
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pgstattuple functions"))));
+
 	/* open relation */
 	rel = relation_open(relid, AccessShareLock);
 
diff --git a/contrib/pgstattuple/pgstattuple.sql.in b/contrib/pgstattuple/pgstattuple.sql.in
index 77a5e2d4b2d..ec8f8b1bbe0 100644
--- a/contrib/pgstattuple/pgstattuple.sql.in
+++ b/contrib/pgstattuple/pgstattuple.sql.in
@@ -1,48 +1,48 @@
 -- Adjust this setting to control where the objects get created.
 SET search_path = public;
 
-CREATE TYPE pgstattuple_type AS (
-	table_len BIGINT,		-- physical table length in bytes
-	tuple_count BIGINT,		-- number of live tuples
-	tuple_len BIGINT,		-- total tuples length in bytes
-	tuple_percent FLOAT,		-- live tuples in %
-	dead_tuple_count BIGINT,	-- number of dead tuples
-	dead_tuple_len BIGINT,		-- total dead tuples length in bytes
-	dead_tuple_percent FLOAT,	-- dead tuples in %
-	free_space BIGINT,		-- free space in bytes
-	free_percent FLOAT		-- free space in %
-);
-
-CREATE OR REPLACE FUNCTION pgstattuple(text)
-RETURNS pgstattuple_type
+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 FLOAT,		-- 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 FLOAT,	-- dead tuples in %
+    OUT free_space BIGINT,		-- free space in bytes
+    OUT free_percent FLOAT)		-- free space in %
 AS 'MODULE_PATHNAME', 'pgstattuple'
 LANGUAGE C STRICT;
 
-CREATE OR REPLACE FUNCTION pgstattuple(oid)
-RETURNS pgstattuple_type
+CREATE OR REPLACE FUNCTION pgstattuple(IN reloid oid,
+    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 FLOAT,		-- 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 FLOAT,	-- dead tuples in %
+    OUT free_space BIGINT,		-- free space in bytes
+    OUT free_percent FLOAT)		-- free space in %
 AS 'MODULE_PATHNAME', 'pgstattuplebyid'
 LANGUAGE C STRICT;
 
 --
 -- pgstatindex
 --
-CREATE TYPE pgstatindex_type AS (
-  version int4,
-  tree_level int4,
-  index_size int4,
-  root_block_no int4,
-  internal_pages int4,
-  leaf_pages int4,
-  empty_pages int4,
-  deleted_pages int4,
-  avg_leaf_density float8,
-  leaf_fragmentation float8
-);
-
-CREATE OR REPLACE FUNCTION pgstatindex(text)
-RETURNS pgstatindex_type
+CREATE OR REPLACE FUNCTION pgstatindex(IN relname text,
+    OUT version int4,
+    OUT tree_level int4,
+    OUT index_size int4,
+    OUT root_block_no int4,
+    OUT internal_pages int4,
+    OUT leaf_pages int4,
+    OUT empty_pages int4,
+    OUT deleted_pages int4,
+    OUT avg_leaf_density float8,
+    OUT leaf_fragmentation float8)
 AS 'MODULE_PATHNAME', 'pgstatindex'
-LANGUAGE 'C' STRICT;
+LANGUAGE C STRICT;
 
 --
 -- pg_relpages()
@@ -50,4 +50,4 @@ LANGUAGE 'C' STRICT;
 CREATE OR REPLACE FUNCTION pg_relpages(text)
 RETURNS int
 AS 'MODULE_PATHNAME', 'pg_relpages'
-LANGUAGE 'C' STRICT;
+LANGUAGE C STRICT;
diff --git a/contrib/pgstattuple/uninstall_pgstattuple.sql b/contrib/pgstattuple/uninstall_pgstattuple.sql
index 16f3d9aa321..6d97590d4d6 100644
--- a/contrib/pgstattuple/uninstall_pgstattuple.sql
+++ b/contrib/pgstattuple/uninstall_pgstattuple.sql
@@ -1,11 +1,7 @@
 -- Adjust this setting to control where the objects get created.
 SET search_path = public;
 
-DROP FUNCTION pgstattuple(oid);
 DROP FUNCTION pgstattuple(text);
-DROP TYPE pgstattuple_type;
-
+DROP FUNCTION pgstattuple(oid);
 DROP FUNCTION pgstatindex(text);
-DROP TYPE pgstatindex_type;
-
 DROP FUNCTION pg_relpages(text);
-- 
GitLab