*** 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 + + + + + + + +