From 1dc118660b8f12d3bdec94b6f6e1068966ca62e9 Mon Sep 17 00:00:00 2001
From: Fujii Masao <fujii@postgresql.org>
Date: Fri, 19 Jul 2013 03:50:20 +0900
Subject: [PATCH] Fix pgstattuple functions to use regclass-type as the
 argument.

This allows us to specify the target relation with several expressions,
'relname', 'schemaname.relname' and OID in all pgstattuple functions.
pgstatindex() and pg_relpages() could not accept OID as the argument
so far.

Per discussion on -hackers, we decided to keep two types of interfaces,
with regclass-type and TEXT-type argument, for each pgstattuple
function because of the backward-compatibility issue. The functions
which have TEXT-type argument will be deprecated in the future release.

Patch by Satoshi Nagayasu, reviewed by Rushabh Lathia and Fujii Masao.
---
 contrib/pgstattuple/Makefile                  |  2 +-
 contrib/pgstattuple/expected/pgstattuple.out  | 84 +++++++++++++++++++
 contrib/pgstattuple/pgstatindex.c             | 72 ++++++++++++++--
 contrib/pgstattuple/pgstattuple--1.1--1.2.sql | 39 +++++++++
 ...tattuple--1.1.sql => pgstattuple--1.2.sql} | 49 +++++++----
 contrib/pgstattuple/pgstattuple.control       |  2 +-
 contrib/pgstattuple/sql/pgstattuple.sql       | 14 ++++
 doc/src/sgml/pgstattuple.sgml                 | 47 +++++++++--
 8 files changed, 280 insertions(+), 29 deletions(-)
 create mode 100644 contrib/pgstattuple/pgstattuple--1.1--1.2.sql
 rename contrib/pgstattuple/{pgstattuple--1.1.sql => pgstattuple--1.2.sql} (75%)

diff --git a/contrib/pgstattuple/Makefile b/contrib/pgstattuple/Makefile
index fc893d8c855..d991c3a8039 100644
--- a/contrib/pgstattuple/Makefile
+++ b/contrib/pgstattuple/Makefile
@@ -4,7 +4,7 @@ MODULE_big	= pgstattuple
 OBJS		= pgstattuple.o pgstatindex.o
 
 EXTENSION = pgstattuple
-DATA = pgstattuple--1.1.sql pgstattuple--1.0--1.1.sql pgstattuple--unpackaged--1.0.sql
+DATA = pgstattuple--1.2.sql pgstattuple--1.1--1.2.sql pgstattuple--1.0--1.1.sql pgstattuple--unpackaged--1.0.sql
 
 REGRESS = pgstattuple
 
diff --git a/contrib/pgstattuple/expected/pgstattuple.out b/contrib/pgstattuple/expected/pgstattuple.out
index ab28f50cb08..7aab9676653 100644
--- a/contrib/pgstattuple/expected/pgstattuple.out
+++ b/contrib/pgstattuple/expected/pgstattuple.out
@@ -5,24 +5,78 @@ CREATE EXTENSION pgstattuple;
 -- indexes should be that.
 --
 create table test (a int primary key, b int[]);
+select * from pgstattuple('test');
+ table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent 
+-----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+--------------
+         0 |           0 |         0 |             0 |                0 |              0 |                  0 |          0 |            0
+(1 row)
+
 select * from pgstattuple('test'::text);
  table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent 
 -----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+--------------
          0 |           0 |         0 |             0 |                0 |              0 |                  0 |          0 |            0
 (1 row)
 
+select * from pgstattuple('test'::name);
+ table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent 
+-----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+--------------
+         0 |           0 |         0 |             0 |                0 |              0 |                  0 |          0 |            0
+(1 row)
+
 select * from pgstattuple('test'::regclass);
  table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent 
 -----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+--------------
          0 |           0 |         0 |             0 |                0 |              0 |                  0 |          0 |            0
 (1 row)
 
+select pgstattuple(oid) from pg_class where relname = 'test';
+     pgstattuple     
+---------------------
+ (0,0,0,0,0,0,0,0,0)
+(1 row)
+
+select pgstattuple(relname) from pg_class where relname = 'test';
+     pgstattuple     
+---------------------
+ (0,0,0,0,0,0,0,0,0)
+(1 row)
+
 select * from pgstatindex('test_pkey');
  version | tree_level | index_size | root_block_no | internal_pages | leaf_pages | empty_pages | deleted_pages | avg_leaf_density | leaf_fragmentation 
 ---------+------------+------------+---------------+----------------+------------+-------------+---------------+------------------+--------------------
        2 |          0 |          0 |             0 |              0 |          0 |           0 |             0 |              NaN |                NaN
 (1 row)
 
+select * from pgstatindex('test_pkey'::text);
+ version | tree_level | index_size | root_block_no | internal_pages | leaf_pages | empty_pages | deleted_pages | avg_leaf_density | leaf_fragmentation 
+---------+------------+------------+---------------+----------------+------------+-------------+---------------+------------------+--------------------
+       2 |          0 |          0 |             0 |              0 |          0 |           0 |             0 |              NaN |                NaN
+(1 row)
+
+select * from pgstatindex('test_pkey'::name);
+ version | tree_level | index_size | root_block_no | internal_pages | leaf_pages | empty_pages | deleted_pages | avg_leaf_density | leaf_fragmentation 
+---------+------------+------------+---------------+----------------+------------+-------------+---------------+------------------+--------------------
+       2 |          0 |          0 |             0 |              0 |          0 |           0 |             0 |              NaN |                NaN
+(1 row)
+
+select * from pgstatindex('test_pkey'::regclass);
+ version | tree_level | index_size | root_block_no | internal_pages | leaf_pages | empty_pages | deleted_pages | avg_leaf_density | leaf_fragmentation 
+---------+------------+------------+---------------+----------------+------------+-------------+---------------+------------------+--------------------
+       2 |          0 |          0 |             0 |              0 |          0 |           0 |             0 |              NaN |                NaN
+(1 row)
+
+select pgstatindex(oid) from pg_class where relname = 'test_pkey';
+        pgstatindex        
+---------------------------
+ (2,0,0,0,0,0,0,0,NaN,NaN)
+(1 row)
+
+select pgstatindex(relname) from pg_class where relname = 'test_pkey';
+        pgstatindex        
+---------------------------
+ (2,0,0,0,0,0,0,0,NaN,NaN)
+(1 row)
+
 select pg_relpages('test');
  pg_relpages 
 -------------
@@ -35,6 +89,36 @@ select pg_relpages('test_pkey');
            1
 (1 row)
 
+select pg_relpages('test_pkey'::text);
+ pg_relpages 
+-------------
+           1
+(1 row)
+
+select pg_relpages('test_pkey'::name);
+ pg_relpages 
+-------------
+           1
+(1 row)
+
+select pg_relpages('test_pkey'::regclass);
+ pg_relpages 
+-------------
+           1
+(1 row)
+
+select pg_relpages(oid) from pg_class where relname = 'test_pkey';
+ pg_relpages 
+-------------
+           1
+(1 row)
+
+select pg_relpages(relname) from pg_class where relname = 'test_pkey';
+ pg_relpages 
+-------------
+           1
+(1 row)
+
 create index test_ginidx on test using gin (b);
 select * from pgstatginindex('test_ginidx');
  version | pending_pages | pending_tuples 
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index 97f897ec1e5..282d82ca828 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -39,12 +39,24 @@
 #include "utils/rel.h"
 
 
+/*
+ * Because of backward-compatibility issue, we have decided to have
+ * two types of interfaces, with regclass-type input arg and text-type
+ * input arg, for each function.
+ *
+ * Those functions which have text-type input arg will be deprecated
+ * in the future release.
+ */
 extern Datum pgstatindex(PG_FUNCTION_ARGS);
+extern Datum pgstatindexbyid(PG_FUNCTION_ARGS);
 extern Datum pg_relpages(PG_FUNCTION_ARGS);
+extern Datum pg_relpagesbyid(PG_FUNCTION_ARGS);
 extern Datum pgstatginindex(PG_FUNCTION_ARGS);
 
 PG_FUNCTION_INFO_V1(pgstatindex);
+PG_FUNCTION_INFO_V1(pgstatindexbyid);
 PG_FUNCTION_INFO_V1(pg_relpages);
+PG_FUNCTION_INFO_V1(pg_relpagesbyid);
 PG_FUNCTION_INFO_V1(pgstatginindex);
 
 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
@@ -97,6 +109,8 @@ typedef struct GinIndexStat
 	int64		pending_tuples;
 } GinIndexStat;
 
+static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo);
+
 /* ------------------------------------------------------
  * pgstatindex()
  *
@@ -109,11 +123,6 @@ pgstatindex(PG_FUNCTION_ARGS)
 	text	   *relname = PG_GETARG_TEXT_P(0);
 	Relation	rel;
 	RangeVar   *relrv;
-	Datum		result;
-	BlockNumber nblocks;
-	BlockNumber blkno;
-	BTIndexStat indexStat;
-	BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
 
 	if (!superuser())
 		ereport(ERROR,
@@ -123,6 +132,34 @@ pgstatindex(PG_FUNCTION_ARGS)
 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 	rel = relation_openrv(relrv, AccessShareLock);
 
+	PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
+}
+
+Datum
+pgstatindexbyid(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"))));
+
+	rel = relation_open(relid, AccessShareLock);
+
+	PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
+}
+
+static Datum
+pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
+{
+	Datum		result;
+	BlockNumber nblocks;
+	BlockNumber blkno;
+	BTIndexStat indexStat;
+	BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
+
 	if (!IS_INDEX(rel) || !IS_BTREE(rel))
 		elog(ERROR, "relation \"%s\" is not a btree index",
 			 RelationGetRelationName(rel));
@@ -274,7 +311,7 @@ pgstatindex(PG_FUNCTION_ARGS)
 		result = HeapTupleGetDatum(tuple);
 	}
 
-	PG_RETURN_DATUM(result);
+	return result;
 }
 
 /* --------------------------------------------------------
@@ -311,6 +348,29 @@ pg_relpages(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(relpages);
 }
 
+Datum
+pg_relpagesbyid(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		relpages;
+	Relation	rel;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pgstattuple functions"))));
+
+	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()
  *
diff --git a/contrib/pgstattuple/pgstattuple--1.1--1.2.sql b/contrib/pgstattuple/pgstattuple--1.1--1.2.sql
new file mode 100644
index 00000000000..2783a63fb15
--- /dev/null
+++ b/contrib/pgstattuple/pgstattuple--1.1--1.2.sql
@@ -0,0 +1,39 @@
+/* contrib/pgstattuple/pgstattuple--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pgstattuple UPDATE TO '1.2'" to load this file. \quit
+
+ALTER EXTENSION pgstattuple DROP FUNCTION pgstattuple(oid);
+DROP FUNCTION pgstattuple(oid);
+
+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;
diff --git a/contrib/pgstattuple/pgstattuple--1.1.sql b/contrib/pgstattuple/pgstattuple--1.2.sql
similarity index 75%
rename from contrib/pgstattuple/pgstattuple--1.1.sql
rename to contrib/pgstattuple/pgstattuple--1.2.sql
index b21fbf83e46..e5fa2f58dab 100644
--- a/contrib/pgstattuple/pgstattuple--1.1.sql
+++ b/contrib/pgstattuple/pgstattuple--1.2.sql
@@ -1,4 +1,4 @@
-/* contrib/pgstattuple/pgstattuple--1.1.sql */
+/* contrib/pgstattuple/pgstattuple--1.2.sql */
 
 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
 \echo Use "CREATE EXTENSION pgstattuple" to load this file. \quit
@@ -16,19 +16,6 @@ CREATE FUNCTION pgstattuple(IN relname text,
 AS 'MODULE_PATHNAME', 'pgstattuple'
 LANGUAGE C STRICT;
 
-CREATE 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 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 text,
     OUT version INT,
     OUT tree_level INT,
@@ -56,3 +43,37 @@ CREATE FUNCTION pgstatginindex(IN relname regclass,
     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;
diff --git a/contrib/pgstattuple/pgstattuple.control b/contrib/pgstattuple/pgstattuple.control
index fcfd36f1b1a..a7cf47fd921 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.1'
+default_version = '1.2'
 module_pathname = '$libdir/pgstattuple'
 relocatable = true
diff --git a/contrib/pgstattuple/sql/pgstattuple.sql b/contrib/pgstattuple/sql/pgstattuple.sql
index 8cb350d6ee1..0e0ad0e15d5 100644
--- a/contrib/pgstattuple/sql/pgstattuple.sql
+++ b/contrib/pgstattuple/sql/pgstattuple.sql
@@ -8,13 +8,27 @@ CREATE EXTENSION pgstattuple;
 
 create table test (a int primary key, b int[]);
 
+select * from pgstattuple('test');
 select * from pgstattuple('test'::text);
+select * from pgstattuple('test'::name);
 select * from pgstattuple('test'::regclass);
+select pgstattuple(oid) from pg_class where relname = 'test';
+select pgstattuple(relname) from pg_class where relname = 'test';
 
 select * from pgstatindex('test_pkey');
+select * from pgstatindex('test_pkey'::text);
+select * from pgstatindex('test_pkey'::name);
+select * from pgstatindex('test_pkey'::regclass);
+select pgstatindex(oid) from pg_class where relname = 'test_pkey';
+select pgstatindex(relname) from pg_class where relname = 'test_pkey';
 
 select pg_relpages('test');
 select pg_relpages('test_pkey');
+select pg_relpages('test_pkey'::text);
+select pg_relpages('test_pkey'::name);
+select pg_relpages('test_pkey'::regclass);
+select pg_relpages(oid) from pg_class where relname = 'test_pkey';
+select pg_relpages(relname) from pg_class where relname = 'test_pkey';
 
 create index test_ginidx on test using gin (b);
 
diff --git a/doc/src/sgml/pgstattuple.sgml b/doc/src/sgml/pgstattuple.sgml
index f2bc2a68f88..6cae04a9c8b 100644
--- a/doc/src/sgml/pgstattuple.sgml
+++ b/doc/src/sgml/pgstattuple.sgml
@@ -22,7 +22,7 @@
    </indexterm>
 
     <term>
-     <function>pgstattuple(text) returns record</>
+     <function>pgstattuple(regclass) returns record</>
     </term>
 
     <listitem>
@@ -30,7 +30,7 @@
       <function>pgstattuple</function> returns a relation's physical length,
       percentage of <quote>dead</> tuples, and other info. This may help users
       to determine whether vacuum is necessary or not.  The argument is the
-      target relation's name (optionally schema-qualified).
+      target relation's name (optionally schema-qualified) or OID.
       For example:
 <programlisting>
 test=> SELECT * FROM pgstattuple('pg_catalog.pg_proc');
@@ -125,13 +125,15 @@ free_percent       | 1.95
 
    <varlistentry>
     <term>
-     <function>pgstattuple(oid) returns record</>
+     <function>pgstattuple(text) returns record</>
     </term>
 
     <listitem>
      <para>
-      This is the same as <function>pgstattuple(text)</function>, except
-      that the target relation is specified by OID.
+      This is the same as <function>pgstattuple(regclass)</function>, except
+      that the target relation is specified by TEXT. This function is kept
+      because of backward-compatibility so far, and will be deprecated in
+      the future release.
      </para>
     </listitem>
    </varlistentry>
@@ -141,7 +143,7 @@ free_percent       | 1.95
     <indexterm>
      <primary>pgstatindex</primary>
     </indexterm>
-     <function>pgstatindex(text) returns record</>
+     <function>pgstatindex(regclass) returns record</>
     </term>
 
     <listitem>
@@ -251,6 +253,21 @@ leaf_fragmentation | 0
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term>
+     <function>pgstatindex(text) returns record</>
+    </term>
+
+    <listitem>
+     <para>
+      This is the same as <function>pgstatindex(regclass)</function>, except
+      that the target index is specified by TEXT. This function is kept
+      because of backward-compatibility so far, and will be deprecated in
+      the future release.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term>
      <indexterm>
@@ -316,7 +333,7 @@ pending_tuples | 0
      <indexterm>
       <primary>pg_relpages</primary>
      </indexterm>
-     <function>pg_relpages(text) returns bigint</>
+     <function>pg_relpages(regclass) returns bigint</>
     </term>
 
     <listitem>
@@ -326,6 +343,22 @@ pending_tuples | 0
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>pg_relpages(text) returns bigint</>
+    </term>
+
+    <listitem>
+     <para>
+      This is the same as <function>pg_relpages(regclass)</function>, except
+      that the target relation is specified by TEXT. This function is kept
+      because of backward-compatibility so far, and will be deprecated in
+      the future release.
+     </para>
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </sect2>
 
-- 
GitLab