diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index b0d5440ca3..b491735d2e 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2451,6 +2451,9 @@ AbortTransaction(void) AbortBufferIO(); UnlockBuffers(); + /* Clear command progress indicator */ + pgstat_progress_end_command(); + /* Reset WAL record construction state */ XLogResetInsertion(); @@ -4540,6 +4543,7 @@ AbortSubTransaction(void) AbortBufferIO(); UnlockBuffers(); + pgstat_progress_end_command(); /* Reset WAL record construction state */ XLogResetInsertion(); diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 363b2d0d10..3bed800362 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -207,6 +207,9 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, else elevel = DEBUG2; + pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, + RelationGetRelid(onerel)); + vac_strategy = bstrategy; vacuum_set_xid_limits(onerel, @@ -320,6 +323,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, onerel->rd_rel->relisshared, new_live_tuples, vacrelstats->new_dead_tuples); + pgstat_progress_end_command(); /* and log the action if appropriate */ if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0) diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index da768c6029..ce5da3e292 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -2731,6 +2731,13 @@ pgstat_bestart(void) beentry->st_clienthostname[NAMEDATALEN - 1] = '\0'; beentry->st_appname[NAMEDATALEN - 1] = '\0'; beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0'; + beentry->st_progress_command = PROGRESS_COMMAND_INVALID; + beentry->st_progress_command_target = InvalidOid; + /* + * we don't zero st_progress_param here to save cycles; nobody should + * examine it until st_progress_command has been set to something other + * than PROGRESS_COMMAND_INVALID + */ pgstat_increment_changecount_after(beentry); @@ -2851,6 +2858,72 @@ pgstat_report_activity(BackendState state, const char *cmd_str) pgstat_increment_changecount_after(beentry); } +/*----------- + * pgstat_progress_start_command() - + * + * Set st_command in own backend entry. Also, zero-initialize + * st_progress_param array. + *----------- + */ +void +pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid) +{ + volatile PgBackendStatus *beentry = MyBEEntry; + + if (!beentry || !pgstat_track_activities) + return; + + pgstat_increment_changecount_before(beentry); + beentry->st_progress_command = cmdtype; + beentry->st_progress_command_target = relid; + MemSet(&beentry->st_progress_param, 0, sizeof(beentry->st_progress_param)); + pgstat_increment_changecount_after(beentry); +} + +/*----------- + * pgstat_progress_update_param() - + * + * Update index'th member in st_progress_param[] of own backend entry. + *----------- + */ +void +pgstat_progress_update_param(int index, int64 val) +{ + volatile PgBackendStatus *beentry = MyBEEntry; + + Assert(index >= 0 && index < PGSTAT_NUM_PROGRESS_PARAM); + + if (!beentry || !pgstat_track_activities) + return; + + pgstat_increment_changecount_before(beentry); + beentry->st_progress_param[index] = val; + pgstat_increment_changecount_after(beentry); +} + +/*----------- + * pgstat_progress_end_command() - + * + * Update index'th member in st_progress_param[] of own backend entry. + *----------- + */ +void +pgstat_progress_end_command(void) +{ + volatile PgBackendStatus *beentry = MyBEEntry; + + if (!beentry) + return; + if (!pgstat_track_activities + && beentry->st_progress_command == PROGRESS_COMMAND_INVALID) + return; + + pgstat_increment_changecount_before(beentry); + beentry->st_progress_command = PROGRESS_COMMAND_INVALID; + beentry->st_progress_command_target = InvalidOid; + pgstat_increment_changecount_after(beentry); +} + /* ---------- * pgstat_report_appname() - * diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 1b22fcc3a1..0c790ff3ec 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -64,6 +64,7 @@ extern Datum pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS); extern Datum pg_stat_get_backend_start(PG_FUNCTION_ARGS); extern Datum pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS); extern Datum pg_stat_get_backend_client_port(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_progress_info(PG_FUNCTION_ARGS); extern Datum pg_stat_get_db_numbackends(PG_FUNCTION_ARGS); extern Datum pg_stat_get_db_xact_commit(PG_FUNCTION_ARGS); @@ -524,6 +525,108 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS) } } +/* + * Returns command progress information for the named command. + */ +Datum +pg_stat_get_progress_info(PG_FUNCTION_ARGS) +{ +#define PG_STAT_GET_PROGRESS_COLS PGSTAT_NUM_PROGRESS_PARAM + 3 + int num_backends = pgstat_fetch_stat_numbackends(); + int curr_backend; + char *cmd = text_to_cstring(PG_GETARG_TEXT_PP(0)); + ProgressCommandType cmdtype; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Translate command name into command type code. */ + if (pg_strcasecmp(cmd, "VACUUM") == 0) + cmdtype = PROGRESS_COMMAND_VACUUM; + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid command name: \"%s\"", cmd))); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + MemoryContextSwitchTo(oldcontext); + + /* 1-based index */ + for (curr_backend = 1; curr_backend <= num_backends; curr_backend++) + { + LocalPgBackendStatus *local_beentry; + PgBackendStatus *beentry; + Datum values[PG_STAT_GET_PROGRESS_COLS]; + bool nulls[PG_STAT_GET_PROGRESS_COLS]; + int i; + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + local_beentry = pgstat_fetch_stat_local_beentry(curr_backend); + + if (!local_beentry) + continue; + + beentry = &local_beentry->backendStatus; + + /* + * Report values for only those backends which are running the given + * command. + */ + if (!beentry || beentry->st_progress_command != cmdtype) + continue; + + /* Value available to all callers */ + values[0] = Int32GetDatum(beentry->st_procpid); + values[1] = ObjectIdGetDatum(beentry->st_databaseid); + + /* show rest of the values including relid only to role members */ + if (has_privs_of_role(GetUserId(), beentry->st_userid)) + { + values[2] = ObjectIdGetDatum(beentry->st_progress_command_target); + for(i = 0; i < PGSTAT_NUM_PROGRESS_PARAM; i++) + values[i+3] = UInt32GetDatum(beentry->st_progress_param[i]); + } + else + { + nulls[2] = true; + for (i = 1; i < PGSTAT_NUM_PROGRESS_PARAM + 1; i++) + nulls[i+3] = true; + } + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} + /* * Returns activity of PG backends. */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 16cd304f1b..62868915dc 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201603051 +#define CATALOG_VERSION_NO 201603091 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index cbbb8835c4..a0f821ac68 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2710,6 +2710,8 @@ DATA(insert OID = 1936 ( pg_stat_get_backend_idset PGNSP PGUID 12 1 100 0 0 f DESCR("statistics: currently active backend IDs"); DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f f f t s r 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ _null_ pg_stat_get_activity _null_ _null_ _null_ )); DESCR("statistics: information about currently active backends"); +DATA(insert OID = 3318 ( pg_stat_get_progress_info PGNSP PGUID 12 1 100 0 0 f f f f f t s r 1 0 2249 "25" "{25,23,26,26,20,20,20,20,20,20,20,20,20,20}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{cmdtype,pid,datid,relid,param1,param2,param3,param4,param5,param6,param7,param8,param9,param10}" _null_ _null_ pg_stat_get_progress_info _null_ _null_ _null_ )); +DESCR("statistics: information about progress of backends running maintenance command"); DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s r 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ )); DESCR("statistics: information about currently active replication"); DATA(insert OID = 3317 ( pg_stat_get_wal_receiver PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{23,25,3220,23,3220,23,1184,1184,3220,1184,25}" "{o,o,o,o,o,o,o,o,o,o,o}" "{pid,status,receive_start_lsn,receive_start_tli,received_lsn,received_tli,last_msg_send_time,last_msg_receipt_time,latest_end_lsn,latest_end_time,slot_name}" _null_ _null_ pg_stat_get_wal_receiver _null_ _null_ _null_ )); diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 65e968eff6..eae6a0fee2 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -695,6 +695,18 @@ typedef enum BackendState STATE_DISABLED } BackendState; +/* ---------- + * Command type for progress reporting purposes + * ---------- + */ +typedef enum ProgressCommandType +{ + PROGRESS_COMMAND_INVALID, + PROGRESS_COMMAND_VACUUM, +} ProgressCommandType; + +#define PGSTAT_NUM_PROGRESS_PARAM 10 + /* ---------- * Shared-memory data structures * ---------- @@ -776,6 +788,19 @@ typedef struct PgBackendStatus /* current command string; MUST be null-terminated */ char *st_activity; + + /* + * Command progress reporting. Any command which wishes can advertise + * that it is running by setting st_progress_command, + * st_progress_command_target, and st_progress_command[]. + * st_progress_command_target should be the OID of the relation which the + * command targets (we assume there's just one, as this is meant for + * utility commands), but the meaning of each element in the + * st_progress_param array is command-specific. + */ + ProgressCommandType st_progress_command; + Oid st_progress_command_target; + int64 st_progress_param[PGSTAT_NUM_PROGRESS_PARAM]; } PgBackendStatus; /* @@ -936,6 +961,11 @@ extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser); extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen); +extern void pgstat_progress_start_command(ProgressCommandType cmdtype, + Oid relid); +extern void pgstat_progress_update_param(int index, int64 val); +extern void pgstat_progress_end_command(void); + extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id); extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);