+ * Support function for the SQL-callable pgstat* functions. Returns
+ * our local copy of the current-activity entry for one backend.
+ *
+ * NB: caller is responsible for a check if the user is permitted to see
+ * this info (especially the querystring).
+ * ----------
+ */
+PgBackendStatus *
+pgstat_fetch_stat_beentry(int beid)
+{
+ pgstat_read_current_status();
+
+ if (beid < 1 || beid > localNumBackends)
+ return NULL;
+
+ return &localBackendStatusTable[beid - 1];
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_numbackends() -
+ *
+ * Support function for the SQL-callable pgstat* functions. Returns
+ * the maximum current backend id.
+ * ----------
+ */
+int
+pgstat_fetch_stat_numbackends(void)
+{
+ pgstat_read_current_status();
+
+ return localNumBackends;
+}
+
+/*
+ * ---------
+ * pgstat_fetch_global() -
+ *
+ * Support function for the SQL-callable pgstat* functions. Returns
+ * a pointer to the global statistics struct.
+ * ---------
+ */
+PgStat_GlobalStats *
+pgstat_fetch_global(void)
+{
+ backend_read_statsfile();
+
+ return &globalStats;
+}
+
+
+/* ------------------------------------------------------------
+ * Functions for management of the shared-memory PgBackendStatus array
+ * ------------------------------------------------------------
+ */
+
+static PgBackendStatus *BackendStatusArray = NULL;
+static PgBackendStatus *MyBEEntry = NULL;
+
+
+/*
+ * Report shared-memory space needed by CreateSharedBackendStatus.
+ */
+Size
+BackendStatusShmemSize(void)
+{
+ Size size;
+
+ size = mul_size(sizeof(PgBackendStatus), MaxBackends);
+ return size;
+}
+
+/*
+ * Initialize the shared status array during postmaster startup.
+ */
+void
+CreateSharedBackendStatus(void)
+{
+ Size size = BackendStatusShmemSize();
+ bool found;
+
+ /* Create or attach to the shared array */
+ BackendStatusArray = (PgBackendStatus *)
+ ShmemInitStruct("Backend Status Array", size, &found);
+
+ if (!found)
+ {
+ /*
+ * We're the first - initialize.
+ */
+ MemSet(BackendStatusArray, 0, size);
+ }
+}
+
+
+/* ----------
+ * pgstat_bestart() -
+ *
+ * Initialize this backend's entry in the PgBackendStatus array,
+ * and set up an on-proc-exit hook that will clear it again.
+ * Called from InitPostgres. MyBackendId and MyDatabaseId must be set.
+ * ----------
+ */
+void
+pgstat_bestart(void)
+{
+ volatile PgBackendStatus *beentry;
+ TimestampTz proc_start_timestamp;
+ Oid userid;
+ SockAddr clientaddr;
+
+ Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
+ MyBEEntry = &BackendStatusArray[MyBackendId - 1];
+
+ /*
+ * To minimize the time spent modifying the entry, fetch all the needed
+ * data first.
+ *
+ * If we have a MyProcPort, use its session start time (for consistency,
+ * and to save a kernel call).
+ */
+ if (MyProcPort)
+ proc_start_timestamp = MyProcPort->SessionStartTime;
+ else
+ proc_start_timestamp = GetCurrentTimestamp();
+ userid = GetSessionUserId();
+
+ /*
+ * We may not have a MyProcPort (eg, if this is the autovacuum process).
+ * If so, use all-zeroes client address, which is dealt with specially in
+ * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port.
+ */
+ if (MyProcPort)
+ memcpy(&clientaddr, &MyProcPort->raddr, sizeof(clientaddr));
+ else
+ MemSet(&clientaddr, 0, sizeof(clientaddr));
+
+ /*
+ * Initialize my status entry, following the protocol of bumping
+ * st_changecount before and after; and make sure it's even afterwards. We
+ * use a volatile pointer here to ensure the compiler doesn't try to get
+ * cute.
+ */
+ beentry = MyBEEntry;
+ do
+ {
+ beentry->st_changecount++;
+ } while ((beentry->st_changecount & 1) == 0);
+
+ beentry->st_procpid = MyProcPid;
+ beentry->st_proc_start_timestamp = proc_start_timestamp;
+ beentry->st_activity_start_timestamp = 0;
+ beentry->st_txn_start_timestamp = 0;
+ beentry->st_databaseid = MyDatabaseId;
+ beentry->st_userid = userid;
+ beentry->st_clientaddr = clientaddr;
+ beentry->st_waiting = false;
+ beentry->st_activity[0] = '\0';
+ /* Also make sure the last byte in the string area is always 0 */
+ beentry->st_activity[PGBE_ACTIVITY_SIZE - 1] = '\0';
+
+ beentry->st_changecount++;
+ Assert((beentry->st_changecount & 1) == 0);
+
+ /*
+ * Set up a process-exit hook to clean up.
+ */
+ on_shmem_exit(pgstat_beshutdown_hook, 0);
+}
+
+/*
+ * Shut down a single backend's statistics reporting at process exit.
+ *
+ * Flush any remaining statistics counts out to the collector.
+ * Without this, operations triggered during backend exit (such as
+ * temp table deletions) won't be counted.
+ *
+ * Lastly, clear out our entry in the PgBackendStatus array.
+ */
+static void
+pgstat_beshutdown_hook(int code, Datum arg)
+{
+ volatile PgBackendStatus *beentry = MyBEEntry;
+
+ pgstat_report_tabstat(true);
+
+ /*
+ * Clear my status entry, following the protocol of bumping st_changecount
+ * before and after. We use a volatile pointer here to ensure the
+ * compiler doesn't try to get cute.
+ */
+ beentry->st_changecount++;
+
+ beentry->st_procpid = 0; /* mark invalid */
+
+ beentry->st_changecount++;
+ Assert((beentry->st_changecount & 1) == 0);
+}
+
+
+/* ----------
+ * pgstat_report_activity() -
+ *
+ * Called from tcop/postgres.c to report what the backend is actually doing
+ * (usually "<IDLE>" or the start of the query to be executed).
+ * ----------
+ */
+void
+pgstat_report_activity(const char *cmd_str)
+{
+ volatile PgBackendStatus *beentry = MyBEEntry;
+ TimestampTz start_timestamp;
+ int len;
+
+ if (!pgstat_collect_querystring || !beentry)
+ return;
+
+ /*
+ * To minimize the time spent modifying the entry, fetch all the needed
+ * data first.
+ */
+ start_timestamp = GetCurrentStatementStartTimestamp();
+
+ len = strlen(cmd_str);
+ len = pg_mbcliplen(cmd_str, len, PGBE_ACTIVITY_SIZE - 1);
+
+ /*
+ * Update my status entry, following the protocol of bumping
+ * st_changecount before and after. We use a volatile pointer here to
+ * ensure the compiler doesn't try to get cute.
+ */
+ beentry->st_changecount++;
+
+ beentry->st_activity_start_timestamp = start_timestamp;
+ memcpy((char *) beentry->st_activity, cmd_str, len);
+ beentry->st_activity[len] = '\0';
+
+ beentry->st_changecount++;
+ Assert((beentry->st_changecount & 1) == 0);
+}
+
+/*
+ * Set the current transaction start timestamp to the specified
+ * value. If there is no current active transaction, this is signified
+ * by 0.
+ */
+void
+pgstat_report_txn_timestamp(TimestampTz tstamp)
+{
+ volatile PgBackendStatus *beentry = MyBEEntry;
+
+ if (!pgstat_collect_querystring || !beentry)
+ return;
+
+ /*
+ * Update my status entry, following the protocol of bumping
+ * st_changecount before and after. We use a volatile pointer
+ * here to ensure the compiler doesn't try to get cute.
+ */
+ beentry->st_changecount++;
+ beentry->st_txn_start_timestamp = tstamp;
+ beentry->st_changecount++;
+ Assert((beentry->st_changecount & 1) == 0);
+}
+
+/* ----------
+ * pgstat_report_waiting() -
+ *
+ * Called from lock manager to report beginning or end of a lock wait.
+ *
+ * NB: this *must* be able to survive being called before MyBEEntry has been
+ * initialized.