aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/monitoring.sgml16
-rw-r--r--src/backend/catalog/system_views.sql3
-rw-r--r--src/backend/postmaster/pgstat.c50
-rw-r--r--src/backend/storage/ipc/sinvaladt.c32
-rw-r--r--src/backend/utils/adt/pgstatfuncs.c27
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.h2
-rw-r--r--src/include/pgstat.h29
-rw-r--r--src/include/storage/sinvaladt.h1
-rw-r--r--src/test/regress/expected/rules.out7
10 files changed, 149 insertions, 20 deletions
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 370857a0e8c..958acc4fd15 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -629,6 +629,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>Toplevel transaction identifier of this backend, if any.</entry>
+ </row>
+ <row>
+ <entry><structfield>backend_xmin</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>The current backend's <literal>xmin</> horizon.</entry>
+ </row>
+ <row>
<entry><structfield>query</></entry>
<entry><type>text</></entry>
<entry>Text of this backend's most recent query. If
@@ -1484,6 +1494,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry>
</row>
<row>
+ <entry><structfield>backend_xid</structfield></entry>
+ <entry><type>xid</type></entry>
+ <entry>This standby's <literal>xmin</> horizon reported
+ by <xref linkend="guc-hot-standby-feedback">.</entry>
+ </row>
+ <row>
<entry><structfield>state</></entry>
<entry><type>text</></entry>
<entry>Current WAL sender state</entry>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a7c6a4e094b..04dfbb0ee54 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -586,6 +586,8 @@ CREATE VIEW pg_stat_activity AS
S.state_change,
S.waiting,
S.state,
+ S.backend_xid,
+ s.backend_xmin,
S.query
FROM pg_database D, pg_stat_get_activity(NULL) AS S, pg_authid U
WHERE S.datid = D.oid AND
@@ -601,6 +603,7 @@ CREATE VIEW pg_stat_replication AS
S.client_hostname,
S.client_port,
S.backend_start,
+ S.backend_xmin,
W.state,
W.sent_location,
W.write_location,
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 305d1269124..06ea5880859 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -48,12 +48,14 @@
#include "postmaster/autovacuum.h"
#include "postmaster/fork_process.h"
#include "postmaster/postmaster.h"
+#include "storage/proc.h"
#include "storage/backendid.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pg_shmem.h"
#include "storage/procsignal.h"
+#include "storage/sinvaladt.h"
#include "utils/ascii.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@@ -213,7 +215,7 @@ typedef struct TwoPhasePgStatRecord
*/
static MemoryContext pgStatLocalContext = NULL;
static HTAB *pgStatDBHash = NULL;
-static PgBackendStatus *localBackendStatusTable = NULL;
+static LocalPgBackendStatus *localBackendStatusTable = NULL;
static int localNumBackends = 0;
/*
@@ -2306,6 +2308,28 @@ pgstat_fetch_stat_beentry(int beid)
if (beid < 1 || beid > localNumBackends)
return NULL;
+ return &localBackendStatusTable[beid - 1].backendStatus;
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_local_beentry() -
+ *
+ * Like pgstat_fetch_stat_beentry() but with locally computed addtions (like
+ * xid and xmin values of the backend)
+ *
+ * NB: caller is responsible for a check if the user is permitted to see
+ * this info (especially the querystring).
+ * ----------
+ */
+LocalPgBackendStatus *
+pgstat_fetch_stat_local_beentry(int beid)
+{
+ pgstat_read_current_status();
+
+ if (beid < 1 || beid > localNumBackends)
+ return NULL;
+
return &localBackendStatusTable[beid - 1];
}
@@ -2783,8 +2807,8 @@ static void
pgstat_read_current_status(void)
{
volatile PgBackendStatus *beentry;
- PgBackendStatus *localtable;
- PgBackendStatus *localentry;
+ LocalPgBackendStatus *localtable;
+ LocalPgBackendStatus *localentry;
char *localappname,
*localactivity;
int i;
@@ -2795,9 +2819,9 @@ pgstat_read_current_status(void)
pgstat_setup_memcxt();
- localtable = (PgBackendStatus *)
+ localtable = (LocalPgBackendStatus *)
MemoryContextAlloc(pgStatLocalContext,
- sizeof(PgBackendStatus) * MaxBackends);
+ sizeof(LocalPgBackendStatus) * MaxBackends);
localappname = (char *)
MemoryContextAlloc(pgStatLocalContext,
NAMEDATALEN * MaxBackends);
@@ -2821,19 +2845,19 @@ pgstat_read_current_status(void)
{
int save_changecount = beentry->st_changecount;
- localentry->st_procpid = beentry->st_procpid;
- if (localentry->st_procpid > 0)
+ localentry->backendStatus.st_procpid = beentry->st_procpid;
+ if (localentry->backendStatus.st_procpid > 0)
{
- memcpy(localentry, (char *) beentry, sizeof(PgBackendStatus));
+ memcpy(&localentry->backendStatus, (char *) beentry, sizeof(PgBackendStatus));
/*
* strcpy is safe even if the string is modified concurrently,
* because there's always a \0 at the end of the buffer.
*/
strcpy(localappname, (char *) beentry->st_appname);
- localentry->st_appname = localappname;
+ localentry->backendStatus.st_appname = localappname;
strcpy(localactivity, (char *) beentry->st_activity);
- localentry->st_activity = localactivity;
+ localentry->backendStatus.st_activity = localactivity;
}
if (save_changecount == beentry->st_changecount &&
@@ -2846,8 +2870,12 @@ pgstat_read_current_status(void)
beentry++;
/* Only valid entries get included into the local array */
- if (localentry->st_procpid > 0)
+ if (localentry->backendStatus.st_procpid > 0)
{
+ BackendIdGetTransactionIds(i,
+ &localentry->backend_xid,
+ &localentry->backend_xmin);
+
localentry++;
localappname += NAMEDATALEN;
localactivity += pgstat_track_activity_query_size;
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index e7c3fb2f548..e6805d96b1c 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -25,6 +25,7 @@
#include "storage/shmem.h"
#include "storage/sinvaladt.h"
#include "storage/spin.h"
+#include "access/transam.h"
/*
@@ -401,6 +402,37 @@ BackendIdGetProc(int backendID)
}
/*
+ * BackendIdGetTransactionIds
+ * Get the xid and xmin of the backend. The result may be out of date
+ * arbitrarily quickly, so the caller must be careful about how this
+ * information is used.
+ */
+void
+BackendIdGetTransactionIds(int backendID, TransactionId *xid, TransactionId *xmin)
+{
+ ProcState *stateP;
+ SISeg *segP = shmInvalBuffer;
+ PGXACT *xact;
+
+ *xid = InvalidTransactionId;
+ *xmin = InvalidTransactionId;
+
+ /* Need to lock out additions/removals of backends */
+ LWLockAcquire(SInvalWriteLock, LW_SHARED);
+
+ if (backendID > 0 && backendID <= segP->lastBackend)
+ {
+ stateP = &segP->procState[backendID - 1];
+ xact = &ProcGlobal->allPgXact[stateP->proc->pgprocno];
+
+ *xid = xact->xid;
+ *xmin = xact->xmin;
+ }
+
+ LWLockRelease(SInvalWriteLock);
+}
+
+/*
* SIInsertDataEntries
* Add new invalidation message(s) to the buffer.
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a4f31cfc7fc..140704f97ca 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -536,7 +536,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
- tupdesc = CreateTemplateTupleDesc(14, false);
+ tupdesc = CreateTemplateTupleDesc(16, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
@@ -565,6 +565,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port",
INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 15, "backend_xid",
+ XIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
+ XIDOID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
@@ -616,9 +620,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
if (funcctx->call_cntr < funcctx->max_calls)
{
/* for each row */
- Datum values[14];
- bool nulls[14];
+ Datum values[16];
+ bool nulls[16];
HeapTuple tuple;
+ LocalPgBackendStatus *local_beentry;
PgBackendStatus *beentry;
MemSet(values, 0, sizeof(values));
@@ -627,12 +632,14 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
if (*(int *) (funcctx->user_fctx) > 0)
{
/* Get specific pid slot */
- beentry = pgstat_fetch_stat_beentry(*(int *) (funcctx->user_fctx));
+ local_beentry = pgstat_fetch_stat_local_beentry(*(int *) (funcctx->user_fctx));
+ beentry = &local_beentry->backendStatus;
}
else
{
/* Get the next one in the list */
- beentry = pgstat_fetch_stat_beentry(funcctx->call_cntr + 1); /* 1-based index */
+ local_beentry = pgstat_fetch_stat_local_beentry(funcctx->call_cntr + 1); /* 1-based index */
+ beentry = &local_beentry->backendStatus;
}
if (!beentry)
{
@@ -657,6 +664,16 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
else
nulls[3] = true;
+ if (TransactionIdIsValid(local_beentry->backend_xid))
+ values[14] = TransactionIdGetDatum(local_beentry->backend_xid);
+ else
+ nulls[14] = true;
+
+ if (TransactionIdIsValid(local_beentry->backend_xmin))
+ values[15] = TransactionIdGetDatum(local_beentry->backend_xmin);
+ else
+ nulls[15] = true;
+
/* Values only available to same user or superuser */
if (superuser() || beentry->st_userid == GetUserId())
{
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 68f96bebaf9..f8c8f9d5ea3 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201402241
+#define CATALOG_VERSION_NO 201402251
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a2cc19fd7b6..e33670ce8e0 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2632,7 +2632,7 @@ DATA(insert OID = 3057 ( pg_stat_get_autoanalyze_count PGNSP PGUID 12 1 0 0 0 f
DESCR("statistics: number of auto analyzes for a table");
DATA(insert OID = 1936 ( pg_stat_get_backend_idset PGNSP PGUID 12 1 100 0 0 f f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ ));
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 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23}" "{i,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}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
+DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,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}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
DESCR("statistics: information about currently active backends");
DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s 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_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
DESCR("statistics: information about currently active replication");
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 5b2e4609f65..932c83d79e5 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -735,6 +735,34 @@ typedef struct PgBackendStatus
char *st_activity;
} PgBackendStatus;
+/* ----------
+ * LocalPgBackendStatus
+ *
+ * When we build the backend status array, we use LocalPgBackendStatus to be
+ * able to add new values to the struct when needed without adding new fields
+ * to the shared memory. It contains the backend status as a first member.
+ * ----------
+ */
+typedef struct LocalPgBackendStatus
+{
+ /*
+ * Local version of the backend status entry.
+ */
+ PgBackendStatus backendStatus;
+
+ /*
+ * The xid of the current transaction if available, InvalidTransactionId
+ * if not.
+ */
+ TransactionId backend_xid;
+
+ /*
+ * The xmin of the current session if available, InvalidTransactionId
+ * if not.
+ */
+ TransactionId backend_xmin;
+} LocalPgBackendStatus;
+
/*
* Working state needed to accumulate per-function-call timing statistics.
*/
@@ -907,6 +935,7 @@ extern void pgstat_send_bgwriter(void);
extern PgStat_StatDBEntry *pgstat_fetch_stat_dbentry(Oid dbid);
extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid);
extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid);
+extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid);
extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
extern int pgstat_fetch_stat_numbackends(void);
extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index 5f2ce488613..9b45b3efef0 100644
--- a/src/include/storage/sinvaladt.h
+++ b/src/include/storage/sinvaladt.h
@@ -32,6 +32,7 @@ extern Size SInvalShmemSize(void);
extern void CreateSharedInvalidationState(void);
extern void SharedInvalBackendInit(bool sendOnly);
extern PGPROC *BackendIdGetProc(int backendID);
+extern void BackendIdGetTransactionIds(int backendID, TransactionId *xid, TransactionId *xmin);
extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n);
extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 220e18b0bbd..ef50f4da217 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1604,9 +1604,11 @@ pg_stat_activity| SELECT s.datid,
s.state_change,
s.waiting,
s.state,
+ s.backend_xid,
+ s.backend_xmin,
s.query
FROM pg_database d,
- pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port),
+ pg_stat_get_activity(NULL::integer) s(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),
pg_authid u
WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1704,6 +1706,7 @@ pg_stat_replication| SELECT s.pid,
s.client_hostname,
s.client_port,
s.backend_start,
+ s.backend_xmin,
w.state,
w.sent_location,
w.write_location,
@@ -1711,7 +1714,7 @@ pg_stat_replication| SELECT s.pid,
w.replay_location,
w.sync_priority,
w.sync_state
- FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port),
+ FROM pg_stat_get_activity(NULL::integer) s(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),
pg_authid u,
pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));