*** a/contrib/pgstattuple/Makefile
--- b/contrib/pgstattuple/Makefile
***************
*** 4,10 **** MODULE_big = pgstattuple
OBJS = pgstattuple.o pgstatindex.o
EXTENSION = pgstattuple
! DATA = pgstattuple--1.0.sql pgstattuple--unpackaged--1.0.sql
REGRESS = pgstattuple
--- 4,10 ----
OBJS = pgstattuple.o pgstatindex.o
EXTENSION = pgstattuple
! DATA = pgstattuple--1.1.sql pgstattuple--1.0--1.1.sql pgstattuple--unpackaged--1.0.sql
REGRESS = pgstattuple
*** a/contrib/pgstattuple/expected/pgstattuple.out
--- b/contrib/pgstattuple/expected/pgstattuple.out
***************
*** 4,10 **** CREATE EXTENSION pgstattuple;
-- the pgstattuple functions, but the results for empty tables and
-- indexes should be that.
--
! create table test (a int primary key);
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
-----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+--------------
--- 4,10 ----
-- the pgstattuple functions, but the results for empty tables and
-- indexes should be that.
--
! create table test (a int primary key, b int[]);
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
-----------+-------------+-----------+---------------+------------------+----------------+--------------------+------------+--------------
***************
*** 35,37 **** select pg_relpages('test_pkey');
--- 35,44 ----
1
(1 row)
+ create index test_ginidx on test using gin (b);
+ select * from pgstatginindex('test_ginidx');
+ version | pending_pages | pending_tuples
+ ---------+---------------+----------------
+ 1 | 0 | 0
+ (1 row)
+
*** a/contrib/pgstattuple/pgstatindex.c
--- b/contrib/pgstattuple/pgstatindex.c
***************
*** 27,32 ****
--- 27,33 ----
#include "postgres.h"
+ #include "access/gin_private.h"
#include "access/heapam.h"
#include "access/nbtree.h"
#include "catalog/namespace.h"
***************
*** 39,50 ****
--- 40,54 ----
extern Datum pgstatindex(PG_FUNCTION_ARGS);
extern Datum pg_relpages(PG_FUNCTION_ARGS);
+ extern Datum pgstatginindex(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pgstatindex);
PG_FUNCTION_INFO_V1(pg_relpages);
+ PG_FUNCTION_INFO_V1(pgstatginindex);
#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)
#define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \
if ( !(FirstOffsetNumber <= (offnum) && \
***************
*** 79,84 **** typedef struct BTIndexStat
--- 83,101 ----
uint64 fragments;
} BTIndexStat;
+ /* ------------------------------------------------
+ * A structure for a whole GIN index statistics
+ * used by pgstatginindex().
+ * ------------------------------------------------
+ */
+ typedef struct GinIndexStat
+ {
+ int32 version;
+
+ BlockNumber pending_pages;
+ int64 pending_tuples;
+ } GinIndexStat;
+
/* ------------------------------------------------------
* pgstatindex()
*
***************
*** 292,294 **** pg_relpages(PG_FUNCTION_ARGS)
--- 309,390 ----
PG_RETURN_INT64(relpages);
}
+
+ /* ------------------------------------------------------
+ * pgstatginindex()
+ *
+ * Usage: SELECT * FROM pgstatginindex('ginindex');
+ * ------------------------------------------------------
+ */
+ Datum
+ pgstatginindex(PG_FUNCTION_ARGS)
+ {
+ Oid relid = PG_GETARG_OID(0);
+ Relation rel;
+ Buffer buffer;
+ Page page;
+ GinMetaPageData *metadata;
+ GinIndexStat stats;
+ HeapTuple tuple;
+ TupleDesc tupleDesc;
+ int j;
+ char *values[3];
+ 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))
+ elog(ERROR, "relation \"%s\" is not a GIN index",
+ RelationGetRelationName(rel));
+
+ /*
+ * Reject attempts to read non-local temporary relations; we would be
+ * likely to get wrong data since we have no visibility into the owning
+ * session's local buffers.
+ */
+ if (RELATION_IS_OTHER_TEMP(rel))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot access temporary indexes of other sessions")));
+
+ /*
+ * Read metapage
+ */
+ buffer = ReadBuffer(rel, GIN_METAPAGE_BLKNO);
+ LockBuffer(buffer, GIN_SHARE);
+ page = BufferGetPage(buffer);
+ metadata = GinPageGetMeta(page);
+
+ stats.version = metadata->ginVersion;
+ stats.pending_pages = metadata->nPendingPages;
+ stats.pending_tuples = metadata->nPendingHeapTuples;
+
+ UnlockReleaseBuffer(buffer);
+ relation_close(rel, AccessShareLock);
+
+ /*
+ * 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);
+ snprintf(values[j++], 32, "%d", stats.version);
+ values[j] = palloc(32);
+ snprintf(values[j++], 32, "%u", stats.pending_pages);
+ values[j] = palloc(64);
+ snprintf(values[j++], 64, INT64_FORMAT, stats.pending_tuples);
+
+ tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
+ values);
+
+ result = HeapTupleGetDatum(tuple);
+
+ PG_RETURN_DATUM(result);
+ }
*** a/contrib/pgstattuple/pgstattuple--1.0.sql
--- /dev/null
***************
*** 1,49 ****
- /* contrib/pgstattuple/pgstattuple--1.0.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 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,
- 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;
--- 0 ----
*** /dev/null
--- b/contrib/pgstattuple/pgstattuple--1.1.sql
***************
*** 0 ****
--- 1,58 ----
+ /* contrib/pgstattuple/pgstattuple--1.1.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 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,
+ 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;
*** a/contrib/pgstattuple/pgstattuple.control
--- b/contrib/pgstattuple/pgstattuple.control
***************
*** 1,5 ****
# pgstattuple extension
comment = 'show tuple-level statistics'
! default_version = '1.0'
module_pathname = '$libdir/pgstattuple'
relocatable = true
--- 1,5 ----
# pgstattuple extension
comment = 'show tuple-level statistics'
! default_version = '1.1'
module_pathname = '$libdir/pgstattuple'
relocatable = true
*** a/contrib/pgstattuple/sql/pgstattuple.sql
--- b/contrib/pgstattuple/sql/pgstattuple.sql
***************
*** 6,12 **** CREATE EXTENSION pgstattuple;
-- indexes should be that.
--
! create table test (a int primary key);
select * from pgstattuple('test'::text);
select * from pgstattuple('test'::regclass);
--- 6,12 ----
-- indexes should be that.
--
! create table test (a int primary key, b int[]);
select * from pgstattuple('test'::text);
select * from pgstattuple('test'::regclass);
***************
*** 15,17 **** select * from pgstatindex('test_pkey');
--- 15,21 ----
select pg_relpages('test');
select pg_relpages('test_pkey');
+
+ create index test_ginidx on test using gin (b);
+
+ select * from pgstatginindex('test_ginidx');
*** a/doc/src/sgml/pgstattuple.sgml
--- b/doc/src/sgml/pgstattuple.sgml
***************
*** 257,262 **** leaf_fragmentation | 0
--- 257,319 ----
+
+
+
+ pgstatginindex(regclass) returns record>
+
+
+
+
+ pgstatginindex returns a record showing information
+ about a GIN index. For example:
+
+ test=> SELECT * FROM pgstatginindex('test_gin_index');
+ -[ RECORD 1 ]--+--
+ version | 1
+ pending_pages | 0
+ pending_tuples | 0
+
+
+
+
+ The output columns are:
+
+
+
+
+
+ Column
+ Type
+ Description
+
+
+
+
+
+ version
+ integer
+ GIN version number
+
+
+
+ pending_pages
+ integer
+ Number of pages in the pending list
+
+
+
+ pending_tuples
+ bigint
+ Number of tuples in the pending list
+
+
+
+
+
+
+
+