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.
This commit is contained in:
Fujii Masao 2013-07-19 03:50:20 +09:00
parent d26888bc4d
commit 1dc118660b
8 changed files with 280 additions and 29 deletions

View File

@ -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

View File

@ -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

View File

@ -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()
*

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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>