diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index d197731..d234080 100644 *** a/doc/src/sgml/monitoring.sgml --- b/doc/src/sgml/monitoring.sgml *************** postgres: user + pg_stat_get_transaction_numscans(oid) + bigint + + Number of sequential scans done when argument is a table, + or number of index scans done when argument is an index, in the current transaction + + + + + pg_stat_get_transaction_tuples_returned(oid) + bigint + + Number of rows read by sequential scans when argument is a table, + or number of index entries returned when argument is an index, in the current transaction + + + + + pg_stat_get_transaction_tuples_fetched(oid) + bigint + + Number of table rows fetched by bitmap scans when argument is a table, + or table rows fetched by simple index scans using the index + when argument is an index, in the current transaction + + + + + pg_stat_get_transaction_tuples_inserted(oid) + bigint + + Number of rows inserted into table, in the current transaction + + + + + pg_stat_get_transaction_tuples_updated(oid) + bigint + + Number of rows updated in table (includes HOT updates), in the current transaction + + + + + pg_stat_get_transaction_tuples_deleted(oid) + bigint + + Number of rows deleted from table, in the current transaction + + + + + pg_stat_get_transaction_tuples_hot_updated(oid) + bigint + + Number of rows HOT-updated in table, in the current transaction + + + + + pg_stat_get_transaction_live_tuples(oid) + bigint + + Number of live rows in table, in the current transaction + + + + + pg_stat_get_transaction_dead_tuples(oid) + bigint + + Number of dead rows in table, in the current transaction + + + + + pg_stat_get_transaction_blocks_fetched(oid) + bigint + + Number of disk block fetch requests for table or index, in the current transaction + + + + + pg_stat_get_transaction_blocks_hit(oid) + bigint + + Number of disk block requests found in cache for table or index, in the current transaction + + + + pg_stat_get_last_vacuum_time(oid) timestamptz *************** postgres: user + pg_stat_get_transaction_function_calls(oid) + bigint + + Number of times the function has been called, in the current transaction. + + + + + pg_stat_get_transaction_function_time(oid) + bigint + + Total wall clock time spent in the function, in microseconds. Includes + the time spent in functions called by this one, in the current transaction. + + + + + pg_stat_get_transaction_function_self_time(oid) + bigint + + Time spent in only this function. Time spent in called functions + is excluded, in the current transaction. + + + + pg_stat_get_backend_idset() setof integer *************** postgres: user + + pg_stat_report_stat() + void + + Report and reset so far collected statistics for the current transaction + (requires superuser privileges) + + + + + + pg_stat_get_blocks_fetched minus diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 8852326..f5f5d42 100644 *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** CREATE VIEW pg_stat_all_tables AS *** 208,213 **** --- 208,236 ---- WHERE C.relkind IN ('r', 't') GROUP BY C.oid, N.nspname, C.relname; + CREATE OR REPLACE VIEW pg_stat_transaction_tables AS + SELECT + C.oid AS relid, + N.nspname AS schemaname, + C.relname AS relname, + pg_stat_get_transaction_numscans(C.oid) AS seq_scan, + pg_stat_get_transaction_tuples_returned(C.oid) AS seq_tup_read, + sum(pg_stat_get_transaction_numscans(I.indexrelid))::bigint AS idx_scan, + sum(pg_stat_get_transaction_tuples_fetched(I.indexrelid))::bigint + + pg_stat_get_transaction_tuples_fetched(C.oid) AS idx_tup_fetch, + pg_stat_get_transaction_tuples_inserted(C.oid) AS n_tup_ins, + pg_stat_get_transaction_tuples_updated(C.oid) AS n_tup_upd, + pg_stat_get_transaction_tuples_deleted(C.oid) AS n_tup_del, + pg_stat_get_transaction_tuples_hot_updated(C.oid) AS n_tup_hot_upd, + pg_stat_get_transaction_live_tuples(C.oid) AS n_live_tup, + pg_stat_get_transaction_dead_tuples(C.oid) AS n_dead_tup + FROM pg_class C LEFT JOIN + pg_index I ON C.oid = I.indrelid + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind IN ('r', 't') AND N.nspname NOT IN ('pg_catalog', 'information_schema') AND + N.nspname !~ '^pg_toast' + GROUP BY C.oid, N.nspname, C.relname; + CREATE VIEW pg_stat_sys_tables AS SELECT * FROM pg_stat_all_tables WHERE schemaname IN ('pg_catalog', 'information_schema') OR *************** CREATE VIEW pg_stat_user_functions AS *** 375,380 **** --- 398,415 ---- WHERE P.prolang != 12 -- fast check to eliminate built-in functions AND pg_stat_get_function_calls(P.oid) IS NOT NULL; + CREATE VIEW pg_stat_transaction_functions AS + SELECT + P.oid AS funcid, + N.nspname AS schemaname, + P.proname AS funcname, + pg_stat_get_transaction_function_calls(P.oid) AS calls, + pg_stat_get_transaction_function_time(P.oid) / 1000 AS total_time, + pg_stat_get_transaction_function_self_time(P.oid) / 1000 AS self_time + FROM pg_proc P LEFT JOIN pg_namespace N ON (N.oid = P.pronamespace) + WHERE P.prolang != 12 -- fast check to eliminate built-in functions + AND pg_stat_get_transaction_function_calls(P.oid) IS NOT NULL; + CREATE VIEW pg_stat_bgwriter AS SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index edb5c80..daa4fbd 100644 *** a/src/backend/postmaster/pgstat.c --- b/src/backend/postmaster/pgstat.c *************** static void pgstat_send_tabstat(PgStat_M *** 258,265 **** static void pgstat_send_funcstats(void); static HTAB *pgstat_collect_oids(Oid catalogid); - static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared); - static void pgstat_setup_memcxt(void); static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype); --- 258,263 ---- *************** pgstat_initstats(Relation rel) *** 1506,1512 **** /* * get_tabstat_entry - find or create a PgStat_TableStatus entry for rel */ ! static PgStat_TableStatus * get_tabstat_entry(Oid rel_id, bool isshared) { PgStat_TableStatus *entry; --- 1504,1510 ---- /* * get_tabstat_entry - find or create a PgStat_TableStatus entry for rel */ ! PgStat_TableStatus * get_tabstat_entry(Oid rel_id, bool isshared) { PgStat_TableStatus *entry; *************** get_tabstat_entry(Oid rel_id, bool issha *** 1561,1566 **** --- 1559,1586 ---- } /* + * get_funcstat_entry - find or create a PgStat_BackendFunctionEntry entry for rel + */ + PgStat_BackendFunctionEntry * + get_funcstat_entry(Oid func_id) + { + PgStat_BackendFunctionEntry *entry; + bool found; + + if (pgStatFunctions == NULL) + return NULL; + + entry = (PgStat_BackendFunctionEntry *) hash_search(pgStatFunctions, + (void *) &func_id, + HASH_FIND, &found); + if (!found) + return NULL; + + return entry; + } + + + /* * get_tabstat_stack_level - add a new (sub)transaction stack entry if needed */ static PgStat_SubXactStatus * diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 8379407..010773d 100644 *** a/src/backend/utils/adt/pgstatfuncs.c --- b/src/backend/utils/adt/pgstatfuncs.c *************** extern Datum pg_stat_reset(PG_FUNCTION_A *** 81,86 **** --- 81,103 ---- extern Datum pg_stat_reset_shared(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS); + extern Datum pg_stat_report_stat(PG_FUNCTION_ARGS); + + extern Datum pg_stat_get_transaction_numscans(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_tuples_returned(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_tuples_fetched(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_tuples_inserted(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_tuples_updated(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_tuples_deleted(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_tuples_hot_updated(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_live_tuples(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_dead_tuples(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_blocks_fetched(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_blocks_hit(PG_FUNCTION_ARGS); + + extern Datum pg_stat_get_transaction_function_calls(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_function_time(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_transaction_function_self_time(PG_FUNCTION_ARGS); /* Global bgwriter statistics, from bgwriter.c */ extern PgStat_MsgBgWriter bgwriterStats; *************** pg_stat_reset_single_function_counters(P *** 1143,1145 **** --- 1160,1380 ---- PG_RETURN_VOID(); } + + + /* Report so far collected per-table and function usage statistics to the collector */ + Datum + pg_stat_report_stat(PG_FUNCTION_ARGS) + { + pgstat_report_stat(true); + + PG_RETURN_VOID(); + } + + + Datum + pg_stat_get_transaction_numscans(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_numscans); + + PG_RETURN_INT64(result); + } + + Datum + pg_stat_get_transaction_tuples_returned(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_tuples_returned); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_tuples_fetched(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_tuples_fetched); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_tuples_inserted(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_tuples_inserted); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_tuples_updated(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_tuples_updated); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_tuples_deleted(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_tuples_deleted); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_tuples_hot_updated(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_tuples_hot_updated); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_live_tuples(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_delta_live_tuples); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_dead_tuples(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_delta_dead_tuples); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_blocks_fetched(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_blocks_fetched); + + PG_RETURN_INT64(result); + } + + + Datum + pg_stat_get_transaction_blocks_hit(PG_FUNCTION_ARGS) + { + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = get_tabstat_entry(relid,false)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_blocks_hit); + + PG_RETURN_INT64(result); + } + + Datum + pg_stat_get_transaction_function_calls(PG_FUNCTION_ARGS) + { + Oid funcid = PG_GETARG_OID(0); + PgStat_BackendFunctionEntry *funcentry; + + if ((funcentry = get_funcstat_entry(funcid)) == NULL) + PG_RETURN_NULL(); + PG_RETURN_INT64(funcentry->f_counts.f_numcalls); + } + + Datum + pg_stat_get_transaction_function_time(PG_FUNCTION_ARGS) + { + Oid funcid = PG_GETARG_OID(0); + PgStat_BackendFunctionEntry *funcentry; + + if ((funcentry = get_funcstat_entry(funcid)) == NULL) + PG_RETURN_NULL(); + PG_RETURN_INT64(INSTR_TIME_GET_MICROSEC(funcentry->f_counts.f_time)); + } + + Datum + pg_stat_get_transaction_function_self_time(PG_FUNCTION_ARGS) + { + Oid funcid = PG_GETARG_OID(0); + PgStat_BackendFunctionEntry *funcentry; + + if ((funcentry = get_funcstat_entry(funcid)) == NULL) + PG_RETURN_NULL(); + PG_RETURN_INT64(INSTR_TIME_GET_MICROSEC(funcentry->f_counts.f_time_self)); + } diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f2751a4..5ed8470 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DESCR("statistics: execution time of fun *** 3091,3096 **** --- 3091,3125 ---- DATA(insert OID = 2980 ( pg_stat_get_function_self_time PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_function_self_time _null_ _null_ _null_ )); DESCR("statistics: self execution time of function"); + DATA(insert OID = 3045 ( pg_stat_get_transaction_numscans PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_numscans _null_ _null_ _null_ )); + DESCR("statistics: number of scans done for table/index in current transaction"); + DATA(insert OID = 3046 ( pg_stat_get_transaction_tuples_returned PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_tuples_returned _null_ _null_ _null_ )); + DESCR("statistics: number of tuples read by seqscan in current transaction"); + DATA(insert OID = 3047 ( pg_stat_get_transaction_tuples_fetched PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_tuples_fetched _null_ _null_ _null_ )); + DESCR("statistics: number of tuples fetched by idxscan in current transaction"); + DATA(insert OID = 3048 ( pg_stat_get_transaction_tuples_inserted PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_tuples_inserted _null_ _null_ _null_ )); + DESCR("statistics: number of tuples inserted in current transaction"); + DATA(insert OID = 3049 ( pg_stat_get_transaction_tuples_updated PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_tuples_updated _null_ _null_ _null_ )); + DESCR("statistics: number of tuples updated in current transaction"); + DATA(insert OID = 3050 ( pg_stat_get_transaction_tuples_deleted PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_tuples_deleted _null_ _null_ _null_ )); + DESCR("statistics: number of tuples deleted in current transaction"); + DATA(insert OID = 3051 ( pg_stat_get_transaction_tuples_hot_updated PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_tuples_hot_updated _null_ _null_ _null_ )); + DESCR("statistics: number of tuples hot updated in current transaction"); + DATA(insert OID = 3052 ( pg_stat_get_transaction_live_tuples PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_live_tuples _null_ _null_ _null_ )); + DESCR("statistics: number of live tuples in current transaction"); + DATA(insert OID = 3053 ( pg_stat_get_transaction_dead_tuples PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_dead_tuples _null_ _null_ _null_ )); + DESCR("statistics: number of dead tuples in current transaction"); + DATA(insert OID = 3054 ( pg_stat_get_transaction_blocks_fetched PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_blocks_fetched _null_ _null_ _null_ )); + DESCR("statistics: number of blocks fetched in current transaction"); + DATA(insert OID = 3055 ( pg_stat_get_transaction_blocks_hit PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_blocks_hit _null_ _null_ _null_ )); + DESCR("statistics: number of blocks found in cache in current transaction"); + DATA(insert OID = 3056 ( pg_stat_get_transaction_function_calls PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_function_calls _null_ _null_ _null_ )); + DESCR("statistics: number of function calls in current transaction"); + DATA(insert OID = 3057 ( pg_stat_get_transaction_function_time PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_function_time _null_ _null_ _null_ )); + DESCR("statistics: execution time of function in current transaction"); + DATA(insert OID = 3058 ( pg_stat_get_transaction_function_self_time PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_transaction_function_self_time _null_ _null_ _null_ )); + DESCR("statistics: self execution time of function in current transaction"); + DATA(insert OID = 2230 ( pg_stat_clear_snapshot PGNSP PGUID 12 1 0 0 f f f f f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_stat_clear_snapshot _null_ _null_ _null_ )); DESCR("statistics: discard current transaction's statistics snapshot"); DATA(insert OID = 2274 ( pg_stat_reset PGNSP PGUID 12 1 0 0 f f f f f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_stat_reset _null_ _null_ _null_ )); *************** DATA(insert OID = 3776 ( pg_stat_reset_ *** 3101,3106 **** --- 3130,3137 ---- DESCR("statistics: reset collected statistics for a single table or index in the current database"); DATA(insert OID = 3777 ( pg_stat_reset_single_function_counters PGNSP PGUID 12 1 0 0 f f f f f v 1 0 2278 "26" _null_ _null_ _null_ _null_ pg_stat_reset_single_function_counters _null_ _null_ _null_ )); DESCR("statistics: reset collected statistics for a single function in the current database"); + DATA(insert OID = 3059 ( pg_stat_report_stat PGNSP PGUID 12 1 0 0 f f f f f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_stat_report_stat _null_ _null_ _null_ )); + DESCR("statistics: report and reset so far collected statistics for current transaction"); DATA(insert OID = 1946 ( encode PGNSP PGUID 12 1 0 0 f f f t f i 2 0 25 "17 25" _null_ _null_ _null_ _null_ binary_encode _null_ _null_ _null_ )); DESCR("convert bytea value into some ascii-only text string"); diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 3dd5f45..5b2f51e 100644 *** a/src/include/pgstat.h --- b/src/include/pgstat.h *************** extern const char *pgstat_get_backend_cu *** 694,699 **** --- 694,702 ---- extern void pgstat_initstats(Relation rel); + extern PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared); + extern PgStat_BackendFunctionEntry *get_funcstat_entry(Oid func_id); + /* nontransactional event counts are simple enough to inline */ #define pgstat_count_heap_scan(rel) \