~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/postmaster/pgstat.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ----------
 
2
 * pgstat.c
 
3
 *
 
4
 *      All the statistics collector stuff hacked up in one big, ugly file.
 
5
 *
 
6
 *      TODO:   - Separate collector, postmaster and backend stuff
 
7
 *                        into different files.
 
8
 *
 
9
 *                      - Add some automatic call for pgstat vacuuming.
 
10
 *
 
11
 *                      - Add a pgstat config column to pg_database, so this
 
12
 *                        entire thing can be enabled/disabled on a per db basis.
 
13
 *
 
14
 *      Copyright (c) 2001-2011, PostgreSQL Global Development Group
 
15
 *
 
16
 *      src/backend/postmaster/pgstat.c
 
17
 * ----------
 
18
 */
 
19
#include "postgres.h"
 
20
 
 
21
#include <unistd.h>
 
22
#include <fcntl.h>
 
23
#include <sys/param.h>
 
24
#include <sys/time.h>
 
25
#include <sys/socket.h>
 
26
#include <netdb.h>
 
27
#include <netinet/in.h>
 
28
#include <arpa/inet.h>
 
29
#include <signal.h>
 
30
#include <time.h>
 
31
#ifdef HAVE_POLL_H
 
32
#include <poll.h>
 
33
#endif
 
34
#ifdef HAVE_SYS_POLL_H
 
35
#include <sys/poll.h>
 
36
#endif
 
37
 
 
38
#include "pgstat.h"
 
39
 
 
40
#include "access/heapam.h"
 
41
#include "access/transam.h"
 
42
#include "access/twophase_rmgr.h"
 
43
#include "access/xact.h"
 
44
#include "catalog/pg_database.h"
 
45
#include "catalog/pg_proc.h"
 
46
#include "libpq/ip.h"
 
47
#include "libpq/libpq.h"
 
48
#include "libpq/pqsignal.h"
 
49
#include "mb/pg_wchar.h"
 
50
#include "miscadmin.h"
 
51
#include "pg_trace.h"
 
52
#include "postmaster/autovacuum.h"
 
53
#include "postmaster/fork_process.h"
 
54
#include "postmaster/postmaster.h"
 
55
#include "storage/backendid.h"
 
56
#include "storage/fd.h"
 
57
#include "storage/ipc.h"
 
58
#include "storage/pg_shmem.h"
 
59
#include "storage/pmsignal.h"
 
60
#include "storage/procsignal.h"
 
61
#include "utils/guc.h"
 
62
#include "utils/memutils.h"
 
63
#include "utils/ps_status.h"
 
64
#include "utils/rel.h"
 
65
#include "utils/tqual.h"
 
66
 
 
67
 
 
68
/* ----------
 
69
 * Paths for the statistics files (relative to installation's $PGDATA).
 
70
 * ----------
 
71
 */
 
72
#define PGSTAT_STAT_PERMANENT_FILENAME          "global/pgstat.stat"
 
73
#define PGSTAT_STAT_PERMANENT_TMPFILE           "global/pgstat.tmp"
 
74
 
 
75
/* ----------
 
76
 * Timer definitions.
 
77
 * ----------
 
78
 */
 
79
#define PGSTAT_STAT_INTERVAL    500             /* Minimum time between stats file
 
80
                                                                                 * updates; in milliseconds. */
 
81
 
 
82
#define PGSTAT_RETRY_DELAY              10              /* How long to wait between statistics
 
83
                                                                                 * update requests; in milliseconds. */
 
84
 
 
85
#define PGSTAT_MAX_WAIT_TIME    5000    /* Maximum time to wait for a stats
 
86
                                                                                 * file update; in milliseconds. */
 
87
 
 
88
#define PGSTAT_RESTART_INTERVAL 60              /* How often to attempt to restart a
 
89
                                                                                 * failed statistics collector; in
 
90
                                                                                 * seconds. */
 
91
 
 
92
#define PGSTAT_SELECT_TIMEOUT   2               /* How often to check for postmaster
 
93
                                                                                 * death; in seconds. */
 
94
 
 
95
#define PGSTAT_POLL_LOOP_COUNT  (PGSTAT_MAX_WAIT_TIME / PGSTAT_RETRY_DELAY)
 
96
 
 
97
 
 
98
/* ----------
 
99
 * The initial size hints for the hash tables used in the collector.
 
100
 * ----------
 
101
 */
 
102
#define PGSTAT_DB_HASH_SIZE             16
 
103
#define PGSTAT_TAB_HASH_SIZE    512
 
104
#define PGSTAT_FUNCTION_HASH_SIZE       512
 
105
 
 
106
 
 
107
/* ----------
 
108
 * GUC parameters
 
109
 * ----------
 
110
 */
 
111
bool            pgstat_track_activities = false;
 
112
bool            pgstat_track_counts = false;
 
113
int                     pgstat_track_functions = TRACK_FUNC_OFF;
 
114
int                     pgstat_track_activity_query_size = 1024;
 
115
 
 
116
/* ----------
 
117
 * Built from GUC parameter
 
118
 * ----------
 
119
 */
 
120
char       *pgstat_stat_filename = NULL;
 
121
char       *pgstat_stat_tmpname = NULL;
 
122
 
 
123
/*
 
124
 * BgWriter global statistics counters (unused in other processes).
 
125
 * Stored directly in a stats message structure so it can be sent
 
126
 * without needing to copy things around.  We assume this inits to zeroes.
 
127
 */
 
128
PgStat_MsgBgWriter BgWriterStats;
 
129
 
 
130
/* ----------
 
131
 * Local data
 
132
 * ----------
 
133
 */
 
134
NON_EXEC_STATIC pgsocket pgStatSock = PGINVALID_SOCKET;
 
135
 
 
136
static struct sockaddr_storage pgStatAddr;
 
137
 
 
138
static time_t last_pgstat_start_time;
 
139
 
 
140
static bool pgStatRunningInCollector = false;
 
141
 
 
142
/*
 
143
 * Structures in which backends store per-table info that's waiting to be
 
144
 * sent to the collector.
 
145
 *
 
146
 * NOTE: once allocated, TabStatusArray structures are never moved or deleted
 
147
 * for the life of the backend.  Also, we zero out the t_id fields of the
 
148
 * contained PgStat_TableStatus structs whenever they are not actively in use.
 
149
 * This allows relcache pgstat_info pointers to be treated as long-lived data,
 
150
 * avoiding repeated searches in pgstat_initstats() when a relation is
 
151
 * repeatedly opened during a transaction.
 
152
 */
 
153
#define TABSTAT_QUANTUM         100 /* we alloc this many at a time */
 
154
 
 
155
typedef struct TabStatusArray
 
156
{
 
157
        struct TabStatusArray *tsa_next;        /* link to next array, if any */
 
158
        int                     tsa_used;               /* # entries currently used */
 
159
        PgStat_TableStatus tsa_entries[TABSTAT_QUANTUM];        /* per-table data */
 
160
} TabStatusArray;
 
161
 
 
162
static TabStatusArray *pgStatTabList = NULL;
 
163
 
 
164
/*
 
165
 * Backends store per-function info that's waiting to be sent to the collector
 
166
 * in this hash table (indexed by function OID).
 
167
 */
 
168
static HTAB *pgStatFunctions = NULL;
 
169
 
 
170
/*
 
171
 * Indicates if backend has some function stats that it hasn't yet
 
172
 * sent to the collector.
 
173
 */
 
174
static bool have_function_stats = false;
 
175
 
 
176
/*
 
177
 * Tuple insertion/deletion counts for an open transaction can't be propagated
 
178
 * into PgStat_TableStatus counters until we know if it is going to commit
 
179
 * or abort.  Hence, we keep these counts in per-subxact structs that live
 
180
 * in TopTransactionContext.  This data structure is designed on the assumption
 
181
 * that subxacts won't usually modify very many tables.
 
182
 */
 
183
typedef struct PgStat_SubXactStatus
 
184
{
 
185
        int                     nest_level;             /* subtransaction nest level */
 
186
        struct PgStat_SubXactStatus *prev;      /* higher-level subxact if any */
 
187
        PgStat_TableXactStatus *first;          /* head of list for this subxact */
 
188
} PgStat_SubXactStatus;
 
189
 
 
190
static PgStat_SubXactStatus *pgStatXactStack = NULL;
 
191
 
 
192
static int      pgStatXactCommit = 0;
 
193
static int      pgStatXactRollback = 0;
 
194
 
 
195
/* Record that's written to 2PC state file when pgstat state is persisted */
 
196
typedef struct TwoPhasePgStatRecord
 
197
{
 
198
        PgStat_Counter tuples_inserted;         /* tuples inserted in xact */
 
199
        PgStat_Counter tuples_updated;          /* tuples updated in xact */
 
200
        PgStat_Counter tuples_deleted;          /* tuples deleted in xact */
 
201
        Oid                     t_id;                   /* table's OID */
 
202
        bool            t_shared;               /* is it a shared catalog? */
 
203
} TwoPhasePgStatRecord;
 
204
 
 
205
/*
 
206
 * Info about current "snapshot" of stats file
 
207
 */
 
208
static MemoryContext pgStatLocalContext = NULL;
 
209
static HTAB *pgStatDBHash = NULL;
 
210
static PgBackendStatus *localBackendStatusTable = NULL;
 
211
static int      localNumBackends = 0;
 
212
 
 
213
/*
 
214
 * Cluster wide statistics, kept in the stats collector.
 
215
 * Contains statistics that are not collected per database
 
216
 * or per table.
 
217
 */
 
218
static PgStat_GlobalStats globalStats;
 
219
 
 
220
/* Last time the collector successfully wrote the stats file */
 
221
static TimestampTz last_statwrite;
 
222
 
 
223
/* Latest statistics request time from backends */
 
224
static TimestampTz last_statrequest;
 
225
 
 
226
static volatile bool need_exit = false;
 
227
static volatile bool got_SIGHUP = false;
 
228
 
 
229
/*
 
230
 * Total time charged to functions so far in the current backend.
 
231
 * We use this to help separate "self" and "other" time charges.
 
232
 * (We assume this initializes to zero.)
 
233
 */
 
234
static instr_time total_func_time;
 
235
 
 
236
 
 
237
/* ----------
 
238
 * Local function forward declarations
 
239
 * ----------
 
240
 */
 
241
#ifdef EXEC_BACKEND
 
242
static pid_t pgstat_forkexec(void);
 
243
#endif
 
244
 
 
245
NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]);
 
246
static void pgstat_exit(SIGNAL_ARGS);
 
247
static void pgstat_beshutdown_hook(int code, Datum arg);
 
248
static void pgstat_sighup_handler(SIGNAL_ARGS);
 
249
 
 
250
static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
 
251
static PgStat_StatTabEntry *pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry,
 
252
                                         Oid tableoid, bool create);
 
253
static void pgstat_write_statsfile(bool permanent);
 
254
static HTAB *pgstat_read_statsfile(Oid onlydb, bool permanent);
 
255
static void backend_read_statsfile(void);
 
256
static void pgstat_read_current_status(void);
 
257
 
 
258
static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
 
259
static void pgstat_send_funcstats(void);
 
260
static HTAB *pgstat_collect_oids(Oid catalogid);
 
261
 
 
262
static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
 
263
 
 
264
static void pgstat_setup_memcxt(void);
 
265
 
 
266
static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype);
 
267
static void pgstat_send(void *msg, int len);
 
268
 
 
269
static void pgstat_recv_inquiry(PgStat_MsgInquiry *msg, int len);
 
270
static void pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len);
 
271
static void pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len);
 
272
static void pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len);
 
273
static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len);
 
274
static void pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len);
 
275
static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len);
 
276
static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len);
 
277
static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len);
 
278
static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len);
 
279
static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len);
 
280
static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
 
281
static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 
282
static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 
283
 
 
284
 
 
285
/* ------------------------------------------------------------
 
286
 * Public functions called from postmaster follow
 
287
 * ------------------------------------------------------------
 
288
 */
 
289
 
 
290
/* ----------
 
291
 * pgstat_init() -
 
292
 *
 
293
 *      Called from postmaster at startup. Create the resources required
 
294
 *      by the statistics collector process.  If unable to do so, do not
 
295
 *      fail --- better to let the postmaster start with stats collection
 
296
 *      disabled.
 
297
 * ----------
 
298
 */
 
299
void
 
300
pgstat_init(void)
 
301
{
 
302
        ACCEPT_TYPE_ARG3 alen;
 
303
        struct addrinfo *addrs = NULL,
 
304
                           *addr,
 
305
                                hints;
 
306
        int                     ret;
 
307
        fd_set          rset;
 
308
        struct timeval tv;
 
309
        char            test_byte;
 
310
        int                     sel_res;
 
311
        int                     tries = 0;
 
312
 
 
313
#define TESTBYTEVAL ((char) 199)
 
314
 
 
315
        /*
 
316
         * Create the UDP socket for sending and receiving statistic messages
 
317
         */
 
318
        hints.ai_flags = AI_PASSIVE;
 
319
        hints.ai_family = PF_UNSPEC;
 
320
        hints.ai_socktype = SOCK_DGRAM;
 
321
        hints.ai_protocol = 0;
 
322
        hints.ai_addrlen = 0;
 
323
        hints.ai_addr = NULL;
 
324
        hints.ai_canonname = NULL;
 
325
        hints.ai_next = NULL;
 
326
        ret = pg_getaddrinfo_all("localhost", NULL, &hints, &addrs);
 
327
        if (ret || !addrs)
 
328
        {
 
329
                ereport(LOG,
 
330
                                (errmsg("could not resolve \"localhost\": %s",
 
331
                                                gai_strerror(ret))));
 
332
                goto startup_failed;
 
333
        }
 
334
 
 
335
        /*
 
336
         * On some platforms, pg_getaddrinfo_all() may return multiple addresses
 
337
         * only one of which will actually work (eg, both IPv6 and IPv4 addresses
 
338
         * when kernel will reject IPv6).  Worse, the failure may occur at the
 
339
         * bind() or perhaps even connect() stage.      So we must loop through the
 
340
         * results till we find a working combination. We will generate LOG
 
341
         * messages, but no error, for bogus combinations.
 
342
         */
 
343
        for (addr = addrs; addr; addr = addr->ai_next)
 
344
        {
 
345
#ifdef HAVE_UNIX_SOCKETS
 
346
                /* Ignore AF_UNIX sockets, if any are returned. */
 
347
                if (addr->ai_family == AF_UNIX)
 
348
                        continue;
 
349
#endif
 
350
 
 
351
                if (++tries > 1)
 
352
                        ereport(LOG,
 
353
                        (errmsg("trying another address for the statistics collector")));
 
354
 
 
355
                /*
 
356
                 * Create the socket.
 
357
                 */
 
358
                if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) == PGINVALID_SOCKET)
 
359
                {
 
360
                        ereport(LOG,
 
361
                                        (errcode_for_socket_access(),
 
362
                        errmsg("could not create socket for statistics collector: %m")));
 
363
                        continue;
 
364
                }
 
365
 
 
366
                /*
 
367
                 * Bind it to a kernel assigned port on localhost and get the assigned
 
368
                 * port via getsockname().
 
369
                 */
 
370
                if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0)
 
371
                {
 
372
                        ereport(LOG,
 
373
                                        (errcode_for_socket_access(),
 
374
                          errmsg("could not bind socket for statistics collector: %m")));
 
375
                        closesocket(pgStatSock);
 
376
                        pgStatSock = PGINVALID_SOCKET;
 
377
                        continue;
 
378
                }
 
379
 
 
380
                alen = sizeof(pgStatAddr);
 
381
                if (getsockname(pgStatSock, (struct sockaddr *) & pgStatAddr, &alen) < 0)
 
382
                {
 
383
                        ereport(LOG,
 
384
                                        (errcode_for_socket_access(),
 
385
                                         errmsg("could not get address of socket for statistics collector: %m")));
 
386
                        closesocket(pgStatSock);
 
387
                        pgStatSock = PGINVALID_SOCKET;
 
388
                        continue;
 
389
                }
 
390
 
 
391
                /*
 
392
                 * Connect the socket to its own address.  This saves a few cycles by
 
393
                 * not having to respecify the target address on every send. This also
 
394
                 * provides a kernel-level check that only packets from this same
 
395
                 * address will be received.
 
396
                 */
 
397
                if (connect(pgStatSock, (struct sockaddr *) & pgStatAddr, alen) < 0)
 
398
                {
 
399
                        ereport(LOG,
 
400
                                        (errcode_for_socket_access(),
 
401
                        errmsg("could not connect socket for statistics collector: %m")));
 
402
                        closesocket(pgStatSock);
 
403
                        pgStatSock = PGINVALID_SOCKET;
 
404
                        continue;
 
405
                }
 
406
 
 
407
                /*
 
408
                 * Try to send and receive a one-byte test message on the socket. This
 
409
                 * is to catch situations where the socket can be created but will not
 
410
                 * actually pass data (for instance, because kernel packet filtering
 
411
                 * rules prevent it).
 
412
                 */
 
413
                test_byte = TESTBYTEVAL;
 
414
 
 
415
retry1:
 
416
                if (send(pgStatSock, &test_byte, 1, 0) != 1)
 
417
                {
 
418
                        if (errno == EINTR)
 
419
                                goto retry1;    /* if interrupted, just retry */
 
420
                        ereport(LOG,
 
421
                                        (errcode_for_socket_access(),
 
422
                                         errmsg("could not send test message on socket for statistics collector: %m")));
 
423
                        closesocket(pgStatSock);
 
424
                        pgStatSock = PGINVALID_SOCKET;
 
425
                        continue;
 
426
                }
 
427
 
 
428
                /*
 
429
                 * There could possibly be a little delay before the message can be
 
430
                 * received.  We arbitrarily allow up to half a second before deciding
 
431
                 * it's broken.
 
432
                 */
 
433
                for (;;)                                /* need a loop to handle EINTR */
 
434
                {
 
435
                        FD_ZERO(&rset);
 
436
                        FD_SET(pgStatSock, &rset);
 
437
 
 
438
                        tv.tv_sec = 0;
 
439
                        tv.tv_usec = 500000;
 
440
                        sel_res = select(pgStatSock + 1, &rset, NULL, NULL, &tv);
 
441
                        if (sel_res >= 0 || errno != EINTR)
 
442
                                break;
 
443
                }
 
444
                if (sel_res < 0)
 
445
                {
 
446
                        ereport(LOG,
 
447
                                        (errcode_for_socket_access(),
 
448
                                         errmsg("select() failed in statistics collector: %m")));
 
449
                        closesocket(pgStatSock);
 
450
                        pgStatSock = PGINVALID_SOCKET;
 
451
                        continue;
 
452
                }
 
453
                if (sel_res == 0 || !FD_ISSET(pgStatSock, &rset))
 
454
                {
 
455
                        /*
 
456
                         * This is the case we actually think is likely, so take pains to
 
457
                         * give a specific message for it.
 
458
                         *
 
459
                         * errno will not be set meaningfully here, so don't use it.
 
460
                         */
 
461
                        ereport(LOG,
 
462
                                        (errcode(ERRCODE_CONNECTION_FAILURE),
 
463
                                         errmsg("test message did not get through on socket for statistics collector")));
 
464
                        closesocket(pgStatSock);
 
465
                        pgStatSock = PGINVALID_SOCKET;
 
466
                        continue;
 
467
                }
 
468
 
 
469
                test_byte++;                    /* just make sure variable is changed */
 
470
 
 
471
retry2:
 
472
                if (recv(pgStatSock, &test_byte, 1, 0) != 1)
 
473
                {
 
474
                        if (errno == EINTR)
 
475
                                goto retry2;    /* if interrupted, just retry */
 
476
                        ereport(LOG,
 
477
                                        (errcode_for_socket_access(),
 
478
                                         errmsg("could not receive test message on socket for statistics collector: %m")));
 
479
                        closesocket(pgStatSock);
 
480
                        pgStatSock = PGINVALID_SOCKET;
 
481
                        continue;
 
482
                }
 
483
 
 
484
                if (test_byte != TESTBYTEVAL)   /* strictly paranoia ... */
 
485
                {
 
486
                        ereport(LOG,
 
487
                                        (errcode(ERRCODE_INTERNAL_ERROR),
 
488
                                         errmsg("incorrect test message transmission on socket for statistics collector")));
 
489
                        closesocket(pgStatSock);
 
490
                        pgStatSock = PGINVALID_SOCKET;
 
491
                        continue;
 
492
                }
 
493
 
 
494
                /* If we get here, we have a working socket */
 
495
                break;
 
496
        }
 
497
 
 
498
        /* Did we find a working address? */
 
499
        if (!addr || pgStatSock == PGINVALID_SOCKET)
 
500
                goto startup_failed;
 
501
 
 
502
        /*
 
503
         * Set the socket to non-blocking IO.  This ensures that if the collector
 
504
         * falls behind, statistics messages will be discarded; backends won't
 
505
         * block waiting to send messages to the collector.
 
506
         */
 
507
        if (!pg_set_noblock(pgStatSock))
 
508
        {
 
509
                ereport(LOG,
 
510
                                (errcode_for_socket_access(),
 
511
                                 errmsg("could not set statistics collector socket to nonblocking mode: %m")));
 
512
                goto startup_failed;
 
513
        }
 
514
 
 
515
        pg_freeaddrinfo_all(hints.ai_family, addrs);
 
516
 
 
517
        return;
 
518
 
 
519
startup_failed:
 
520
        ereport(LOG,
 
521
          (errmsg("disabling statistics collector for lack of working socket")));
 
522
 
 
523
        if (addrs)
 
524
                pg_freeaddrinfo_all(hints.ai_family, addrs);
 
525
 
 
526
        if (pgStatSock != PGINVALID_SOCKET)
 
527
                closesocket(pgStatSock);
 
528
        pgStatSock = PGINVALID_SOCKET;
 
529
 
 
530
        /*
 
531
         * Adjust GUC variables to suppress useless activity, and for debugging
 
532
         * purposes (seeing track_counts off is a clue that we failed here). We
 
533
         * use PGC_S_OVERRIDE because there is no point in trying to turn it back
 
534
         * on from postgresql.conf without a restart.
 
535
         */
 
536
        SetConfigOption("track_counts", "off", PGC_INTERNAL, PGC_S_OVERRIDE);
 
537
}
 
538
 
 
539
/*
 
540
 * pgstat_reset_all() -
 
541
 *
 
542
 * Remove the stats file.  This is currently used only if WAL
 
543
 * recovery is needed after a crash.
 
544
 */
 
545
void
 
546
pgstat_reset_all(void)
 
547
{
 
548
        unlink(pgstat_stat_filename);
 
549
        unlink(PGSTAT_STAT_PERMANENT_FILENAME);
 
550
}
 
551
 
 
552
#ifdef EXEC_BACKEND
 
553
 
 
554
/*
 
555
 * pgstat_forkexec() -
 
556
 *
 
557
 * Format up the arglist for, then fork and exec, statistics collector process
 
558
 */
 
559
static pid_t
 
560
pgstat_forkexec(void)
 
561
{
 
562
        char       *av[10];
 
563
        int                     ac = 0;
 
564
 
 
565
        av[ac++] = "postgres";
 
566
        av[ac++] = "--forkcol";
 
567
        av[ac++] = NULL;                        /* filled in by postmaster_forkexec */
 
568
 
 
569
        av[ac] = NULL;
 
570
        Assert(ac < lengthof(av));
 
571
 
 
572
        return postmaster_forkexec(ac, av);
 
573
}
 
574
#endif   /* EXEC_BACKEND */
 
575
 
 
576
 
 
577
/*
 
578
 * pgstat_start() -
 
579
 *
 
580
 *      Called from postmaster at startup or after an existing collector
 
581
 *      died.  Attempt to fire up a fresh statistics collector.
 
582
 *
 
583
 *      Returns PID of child process, or 0 if fail.
 
584
 *
 
585
 *      Note: if fail, we will be called again from the postmaster main loop.
 
586
 */
 
587
int
 
588
pgstat_start(void)
 
589
{
 
590
        time_t          curtime;
 
591
        pid_t           pgStatPid;
 
592
 
 
593
        /*
 
594
         * Check that the socket is there, else pgstat_init failed and we can do
 
595
         * nothing useful.
 
596
         */
 
597
        if (pgStatSock == PGINVALID_SOCKET)
 
598
                return 0;
 
599
 
 
600
        /*
 
601
         * Do nothing if too soon since last collector start.  This is a safety
 
602
         * valve to protect against continuous respawn attempts if the collector
 
603
         * is dying immediately at launch.      Note that since we will be re-called
 
604
         * from the postmaster main loop, we will get another chance later.
 
605
         */
 
606
        curtime = time(NULL);
 
607
        if ((unsigned int) (curtime - last_pgstat_start_time) <
 
608
                (unsigned int) PGSTAT_RESTART_INTERVAL)
 
609
                return 0;
 
610
        last_pgstat_start_time = curtime;
 
611
 
 
612
        /*
 
613
         * Okay, fork off the collector.
 
614
         */
 
615
#ifdef EXEC_BACKEND
 
616
        switch ((pgStatPid = pgstat_forkexec()))
 
617
#else
 
618
        switch ((pgStatPid = fork_process()))
 
619
#endif
 
620
        {
 
621
                case -1:
 
622
                        ereport(LOG,
 
623
                                        (errmsg("could not fork statistics collector: %m")));
 
624
                        return 0;
 
625
 
 
626
#ifndef EXEC_BACKEND
 
627
                case 0:
 
628
                        /* in postmaster child ... */
 
629
                        /* Close the postmaster's sockets */
 
630
                        ClosePostmasterPorts(false);
 
631
 
 
632
                        /* Lose the postmaster's on-exit routines */
 
633
                        on_exit_reset();
 
634
 
 
635
                        /* Drop our connection to postmaster's shared memory, as well */
 
636
                        PGSharedMemoryDetach();
 
637
 
 
638
                        PgstatCollectorMain(0, NULL);
 
639
                        break;
 
640
#endif
 
641
 
 
642
                default:
 
643
                        return (int) pgStatPid;
 
644
        }
 
645
 
 
646
        /* shouldn't get here */
 
647
        return 0;
 
648
}
 
649
 
 
650
void
 
651
allow_immediate_pgstat_restart(void)
 
652
{
 
653
        last_pgstat_start_time = 0;
 
654
}
 
655
 
 
656
/* ------------------------------------------------------------
 
657
 * Public functions used by backends follow
 
658
 *------------------------------------------------------------
 
659
 */
 
660
 
 
661
 
 
662
/* ----------
 
663
 * pgstat_report_stat() -
 
664
 *
 
665
 *      Called from tcop/postgres.c to send the so far collected per-table
 
666
 *      and function usage statistics to the collector.  Note that this is
 
667
 *      called only when not within a transaction, so it is fair to use
 
668
 *      transaction stop time as an approximation of current time.
 
669
 * ----------
 
670
 */
 
671
void
 
672
pgstat_report_stat(bool force)
 
673
{
 
674
        /* we assume this inits to all zeroes: */
 
675
        static const PgStat_TableCounts all_zeroes;
 
676
        static TimestampTz last_report = 0;
 
677
 
 
678
        TimestampTz now;
 
679
        PgStat_MsgTabstat regular_msg;
 
680
        PgStat_MsgTabstat shared_msg;
 
681
        TabStatusArray *tsa;
 
682
        int                     i;
 
683
 
 
684
        /* Don't expend a clock check if nothing to do */
 
685
        if ((pgStatTabList == NULL || pgStatTabList->tsa_used == 0)
 
686
                && !have_function_stats)
 
687
                return;
 
688
 
 
689
        /*
 
690
         * Don't send a message unless it's been at least PGSTAT_STAT_INTERVAL
 
691
         * msec since we last sent one, or the caller wants to force stats out.
 
692
         */
 
693
        now = GetCurrentTransactionStopTimestamp();
 
694
        if (!force &&
 
695
                !TimestampDifferenceExceeds(last_report, now, PGSTAT_STAT_INTERVAL))
 
696
                return;
 
697
        last_report = now;
 
698
 
 
699
        /*
 
700
         * Scan through the TabStatusArray struct(s) to find tables that actually
 
701
         * have counts, and build messages to send.  We have to separate shared
 
702
         * relations from regular ones because the databaseid field in the message
 
703
         * header has to depend on that.
 
704
         */
 
705
        regular_msg.m_databaseid = MyDatabaseId;
 
706
        shared_msg.m_databaseid = InvalidOid;
 
707
        regular_msg.m_nentries = 0;
 
708
        shared_msg.m_nentries = 0;
 
709
 
 
710
        for (tsa = pgStatTabList; tsa != NULL; tsa = tsa->tsa_next)
 
711
        {
 
712
                for (i = 0; i < tsa->tsa_used; i++)
 
713
                {
 
714
                        PgStat_TableStatus *entry = &tsa->tsa_entries[i];
 
715
                        PgStat_MsgTabstat *this_msg;
 
716
                        PgStat_TableEntry *this_ent;
 
717
 
 
718
                        /* Shouldn't have any pending transaction-dependent counts */
 
719
                        Assert(entry->trans == NULL);
 
720
 
 
721
                        /*
 
722
                         * Ignore entries that didn't accumulate any actual counts, such
 
723
                         * as indexes that were opened by the planner but not used.
 
724
                         */
 
725
                        if (memcmp(&entry->t_counts, &all_zeroes,
 
726
                                           sizeof(PgStat_TableCounts)) == 0)
 
727
                                continue;
 
728
 
 
729
                        /*
 
730
                         * OK, insert data into the appropriate message, and send if full.
 
731
                         */
 
732
                        this_msg = entry->t_shared ? &shared_msg : &regular_msg;
 
733
                        this_ent = &this_msg->m_entry[this_msg->m_nentries];
 
734
                        this_ent->t_id = entry->t_id;
 
735
                        memcpy(&this_ent->t_counts, &entry->t_counts,
 
736
                                   sizeof(PgStat_TableCounts));
 
737
                        if (++this_msg->m_nentries >= PGSTAT_NUM_TABENTRIES)
 
738
                        {
 
739
                                pgstat_send_tabstat(this_msg);
 
740
                                this_msg->m_nentries = 0;
 
741
                        }
 
742
                }
 
743
                /* zero out TableStatus structs after use */
 
744
                MemSet(tsa->tsa_entries, 0,
 
745
                           tsa->tsa_used * sizeof(PgStat_TableStatus));
 
746
                tsa->tsa_used = 0;
 
747
        }
 
748
 
 
749
        /*
 
750
         * Send partial messages.  If force is true, make sure that any pending
 
751
         * xact commit/abort gets counted, even if no table stats to send.
 
752
         */
 
753
        if (regular_msg.m_nentries > 0 ||
 
754
                (force && (pgStatXactCommit > 0 || pgStatXactRollback > 0)))
 
755
                pgstat_send_tabstat(&regular_msg);
 
756
        if (shared_msg.m_nentries > 0)
 
757
                pgstat_send_tabstat(&shared_msg);
 
758
 
 
759
        /* Now, send function statistics */
 
760
        pgstat_send_funcstats();
 
761
}
 
762
 
 
763
/*
 
764
 * Subroutine for pgstat_report_stat: finish and send a tabstat message
 
765
 */
 
766
static void
 
767
pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg)
 
768
{
 
769
        int                     n;
 
770
        int                     len;
 
771
 
 
772
        /* It's unlikely we'd get here with no socket, but maybe not impossible */
 
773
        if (pgStatSock == PGINVALID_SOCKET)
 
774
                return;
 
775
 
 
776
        /*
 
777
         * Report accumulated xact commit/rollback whenever we send a normal
 
778
         * tabstat message
 
779
         */
 
780
        if (OidIsValid(tsmsg->m_databaseid))
 
781
        {
 
782
                tsmsg->m_xact_commit = pgStatXactCommit;
 
783
                tsmsg->m_xact_rollback = pgStatXactRollback;
 
784
                pgStatXactCommit = 0;
 
785
                pgStatXactRollback = 0;
 
786
        }
 
787
        else
 
788
        {
 
789
                tsmsg->m_xact_commit = 0;
 
790
                tsmsg->m_xact_rollback = 0;
 
791
        }
 
792
 
 
793
        n = tsmsg->m_nentries;
 
794
        len = offsetof(PgStat_MsgTabstat, m_entry[0]) +
 
795
                n * sizeof(PgStat_TableEntry);
 
796
 
 
797
        pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT);
 
798
        pgstat_send(tsmsg, len);
 
799
}
 
800
 
 
801
/*
 
802
 * Subroutine for pgstat_report_stat: populate and send a function stat message
 
803
 */
 
804
static void
 
805
pgstat_send_funcstats(void)
 
806
{
 
807
        /* we assume this inits to all zeroes: */
 
808
        static const PgStat_FunctionCounts all_zeroes;
 
809
 
 
810
        PgStat_MsgFuncstat msg;
 
811
        PgStat_BackendFunctionEntry *entry;
 
812
        HASH_SEQ_STATUS fstat;
 
813
 
 
814
        if (pgStatFunctions == NULL)
 
815
                return;
 
816
 
 
817
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_FUNCSTAT);
 
818
        msg.m_databaseid = MyDatabaseId;
 
819
        msg.m_nentries = 0;
 
820
 
 
821
        hash_seq_init(&fstat, pgStatFunctions);
 
822
        while ((entry = (PgStat_BackendFunctionEntry *) hash_seq_search(&fstat)) != NULL)
 
823
        {
 
824
                PgStat_FunctionEntry *m_ent;
 
825
 
 
826
                /* Skip it if no counts accumulated since last time */
 
827
                if (memcmp(&entry->f_counts, &all_zeroes,
 
828
                                   sizeof(PgStat_FunctionCounts)) == 0)
 
829
                        continue;
 
830
 
 
831
                /* need to convert format of time accumulators */
 
832
                m_ent = &msg.m_entry[msg.m_nentries];
 
833
                m_ent->f_id = entry->f_id;
 
834
                m_ent->f_numcalls = entry->f_counts.f_numcalls;
 
835
                m_ent->f_time = INSTR_TIME_GET_MICROSEC(entry->f_counts.f_time);
 
836
                m_ent->f_time_self = INSTR_TIME_GET_MICROSEC(entry->f_counts.f_time_self);
 
837
 
 
838
                if (++msg.m_nentries >= PGSTAT_NUM_FUNCENTRIES)
 
839
                {
 
840
                        pgstat_send(&msg, offsetof(PgStat_MsgFuncstat, m_entry[0]) +
 
841
                                                msg.m_nentries * sizeof(PgStat_FunctionEntry));
 
842
                        msg.m_nentries = 0;
 
843
                }
 
844
 
 
845
                /* reset the entry's counts */
 
846
                MemSet(&entry->f_counts, 0, sizeof(PgStat_FunctionCounts));
 
847
        }
 
848
 
 
849
        if (msg.m_nentries > 0)
 
850
                pgstat_send(&msg, offsetof(PgStat_MsgFuncstat, m_entry[0]) +
 
851
                                        msg.m_nentries * sizeof(PgStat_FunctionEntry));
 
852
 
 
853
        have_function_stats = false;
 
854
}
 
855
 
 
856
 
 
857
/* ----------
 
858
 * pgstat_vacuum_stat() -
 
859
 *
 
860
 *      Will tell the collector about objects he can get rid of.
 
861
 * ----------
 
862
 */
 
863
void
 
864
pgstat_vacuum_stat(void)
 
865
{
 
866
        HTAB       *htab;
 
867
        PgStat_MsgTabpurge msg;
 
868
        PgStat_MsgFuncpurge f_msg;
 
869
        HASH_SEQ_STATUS hstat;
 
870
        PgStat_StatDBEntry *dbentry;
 
871
        PgStat_StatTabEntry *tabentry;
 
872
        PgStat_StatFuncEntry *funcentry;
 
873
        int                     len;
 
874
 
 
875
        if (pgStatSock == PGINVALID_SOCKET)
 
876
                return;
 
877
 
 
878
        /*
 
879
         * If not done for this transaction, read the statistics collector stats
 
880
         * file into some hash tables.
 
881
         */
 
882
        backend_read_statsfile();
 
883
 
 
884
        /*
 
885
         * Read pg_database and make a list of OIDs of all existing databases
 
886
         */
 
887
        htab = pgstat_collect_oids(DatabaseRelationId);
 
888
 
 
889
        /*
 
890
         * Search the database hash table for dead databases and tell the
 
891
         * collector to drop them.
 
892
         */
 
893
        hash_seq_init(&hstat, pgStatDBHash);
 
894
        while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL)
 
895
        {
 
896
                Oid                     dbid = dbentry->databaseid;
 
897
 
 
898
                CHECK_FOR_INTERRUPTS();
 
899
 
 
900
                /* the DB entry for shared tables (with InvalidOid) is never dropped */
 
901
                if (OidIsValid(dbid) &&
 
902
                        hash_search(htab, (void *) &dbid, HASH_FIND, NULL) == NULL)
 
903
                        pgstat_drop_database(dbid);
 
904
        }
 
905
 
 
906
        /* Clean up */
 
907
        hash_destroy(htab);
 
908
 
 
909
        /*
 
910
         * Lookup our own database entry; if not found, nothing more to do.
 
911
         */
 
912
        dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
 
913
                                                                                                 (void *) &MyDatabaseId,
 
914
                                                                                                 HASH_FIND, NULL);
 
915
        if (dbentry == NULL || dbentry->tables == NULL)
 
916
                return;
 
917
 
 
918
        /*
 
919
         * Similarly to above, make a list of all known relations in this DB.
 
920
         */
 
921
        htab = pgstat_collect_oids(RelationRelationId);
 
922
 
 
923
        /*
 
924
         * Initialize our messages table counter to zero
 
925
         */
 
926
        msg.m_nentries = 0;
 
927
 
 
928
        /*
 
929
         * Check for all tables listed in stats hashtable if they still exist.
 
930
         */
 
931
        hash_seq_init(&hstat, dbentry->tables);
 
932
        while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL)
 
933
        {
 
934
                Oid                     tabid = tabentry->tableid;
 
935
 
 
936
                CHECK_FOR_INTERRUPTS();
 
937
 
 
938
                if (hash_search(htab, (void *) &tabid, HASH_FIND, NULL) != NULL)
 
939
                        continue;
 
940
 
 
941
                /*
 
942
                 * Not there, so add this table's Oid to the message
 
943
                 */
 
944
                msg.m_tableid[msg.m_nentries++] = tabid;
 
945
 
 
946
                /*
 
947
                 * If the message is full, send it out and reinitialize to empty
 
948
                 */
 
949
                if (msg.m_nentries >= PGSTAT_NUM_TABPURGE)
 
950
                {
 
951
                        len = offsetof(PgStat_MsgTabpurge, m_tableid[0])
 
952
                                +msg.m_nentries * sizeof(Oid);
 
953
 
 
954
                        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE);
 
955
                        msg.m_databaseid = MyDatabaseId;
 
956
                        pgstat_send(&msg, len);
 
957
 
 
958
                        msg.m_nentries = 0;
 
959
                }
 
960
        }
 
961
 
 
962
        /*
 
963
         * Send the rest
 
964
         */
 
965
        if (msg.m_nentries > 0)
 
966
        {
 
967
                len = offsetof(PgStat_MsgTabpurge, m_tableid[0])
 
968
                        +msg.m_nentries * sizeof(Oid);
 
969
 
 
970
                pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE);
 
971
                msg.m_databaseid = MyDatabaseId;
 
972
                pgstat_send(&msg, len);
 
973
        }
 
974
 
 
975
        /* Clean up */
 
976
        hash_destroy(htab);
 
977
 
 
978
        /*
 
979
         * Now repeat the above steps for functions.  However, we needn't bother
 
980
         * in the common case where no function stats are being collected.
 
981
         */
 
982
        if (dbentry->functions != NULL &&
 
983
                hash_get_num_entries(dbentry->functions) > 0)
 
984
        {
 
985
                htab = pgstat_collect_oids(ProcedureRelationId);
 
986
 
 
987
                pgstat_setheader(&f_msg.m_hdr, PGSTAT_MTYPE_FUNCPURGE);
 
988
                f_msg.m_databaseid = MyDatabaseId;
 
989
                f_msg.m_nentries = 0;
 
990
 
 
991
                hash_seq_init(&hstat, dbentry->functions);
 
992
                while ((funcentry = (PgStat_StatFuncEntry *) hash_seq_search(&hstat)) != NULL)
 
993
                {
 
994
                        Oid                     funcid = funcentry->functionid;
 
995
 
 
996
                        CHECK_FOR_INTERRUPTS();
 
997
 
 
998
                        if (hash_search(htab, (void *) &funcid, HASH_FIND, NULL) != NULL)
 
999
                                continue;
 
1000
 
 
1001
                        /*
 
1002
                         * Not there, so add this function's Oid to the message
 
1003
                         */
 
1004
                        f_msg.m_functionid[f_msg.m_nentries++] = funcid;
 
1005
 
 
1006
                        /*
 
1007
                         * If the message is full, send it out and reinitialize to empty
 
1008
                         */
 
1009
                        if (f_msg.m_nentries >= PGSTAT_NUM_FUNCPURGE)
 
1010
                        {
 
1011
                                len = offsetof(PgStat_MsgFuncpurge, m_functionid[0])
 
1012
                                        +f_msg.m_nentries * sizeof(Oid);
 
1013
 
 
1014
                                pgstat_send(&f_msg, len);
 
1015
 
 
1016
                                f_msg.m_nentries = 0;
 
1017
                        }
 
1018
                }
 
1019
 
 
1020
                /*
 
1021
                 * Send the rest
 
1022
                 */
 
1023
                if (f_msg.m_nentries > 0)
 
1024
                {
 
1025
                        len = offsetof(PgStat_MsgFuncpurge, m_functionid[0])
 
1026
                                +f_msg.m_nentries * sizeof(Oid);
 
1027
 
 
1028
                        pgstat_send(&f_msg, len);
 
1029
                }
 
1030
 
 
1031
                hash_destroy(htab);
 
1032
        }
 
1033
}
 
1034
 
 
1035
 
 
1036
/* ----------
 
1037
 * pgstat_collect_oids() -
 
1038
 *
 
1039
 *      Collect the OIDs of all objects listed in the specified system catalog
 
1040
 *      into a temporary hash table.  Caller should hash_destroy the result
 
1041
 *      when done with it.      (However, we make the table in CurrentMemoryContext
 
1042
 *      so that it will be freed properly in event of an error.)
 
1043
 * ----------
 
1044
 */
 
1045
static HTAB *
 
1046
pgstat_collect_oids(Oid catalogid)
 
1047
{
 
1048
        HTAB       *htab;
 
1049
        HASHCTL         hash_ctl;
 
1050
        Relation        rel;
 
1051
        HeapScanDesc scan;
 
1052
        HeapTuple       tup;
 
1053
 
 
1054
        memset(&hash_ctl, 0, sizeof(hash_ctl));
 
1055
        hash_ctl.keysize = sizeof(Oid);
 
1056
        hash_ctl.entrysize = sizeof(Oid);
 
1057
        hash_ctl.hash = oid_hash;
 
1058
        hash_ctl.hcxt = CurrentMemoryContext;
 
1059
        htab = hash_create("Temporary table of OIDs",
 
1060
                                           PGSTAT_TAB_HASH_SIZE,
 
1061
                                           &hash_ctl,
 
1062
                                           HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
 
1063
 
 
1064
        rel = heap_open(catalogid, AccessShareLock);
 
1065
        scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
 
1066
        while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
 
1067
        {
 
1068
                Oid                     thisoid = HeapTupleGetOid(tup);
 
1069
 
 
1070
                CHECK_FOR_INTERRUPTS();
 
1071
 
 
1072
                (void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
 
1073
        }
 
1074
        heap_endscan(scan);
 
1075
        heap_close(rel, AccessShareLock);
 
1076
 
 
1077
        return htab;
 
1078
}
 
1079
 
 
1080
 
 
1081
/* ----------
 
1082
 * pgstat_drop_database() -
 
1083
 *
 
1084
 *      Tell the collector that we just dropped a database.
 
1085
 *      (If the message gets lost, we will still clean the dead DB eventually
 
1086
 *      via future invocations of pgstat_vacuum_stat().)
 
1087
 * ----------
 
1088
 */
 
1089
void
 
1090
pgstat_drop_database(Oid databaseid)
 
1091
{
 
1092
        PgStat_MsgDropdb msg;
 
1093
 
 
1094
        if (pgStatSock == PGINVALID_SOCKET)
 
1095
                return;
 
1096
 
 
1097
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DROPDB);
 
1098
        msg.m_databaseid = databaseid;
 
1099
        pgstat_send(&msg, sizeof(msg));
 
1100
}
 
1101
 
 
1102
 
 
1103
/* ----------
 
1104
 * pgstat_drop_relation() -
 
1105
 *
 
1106
 *      Tell the collector that we just dropped a relation.
 
1107
 *      (If the message gets lost, we will still clean the dead entry eventually
 
1108
 *      via future invocations of pgstat_vacuum_stat().)
 
1109
 *
 
1110
 *      Currently not used for lack of any good place to call it; we rely
 
1111
 *      entirely on pgstat_vacuum_stat() to clean out stats for dead rels.
 
1112
 * ----------
 
1113
 */
 
1114
#ifdef NOT_USED
 
1115
void
 
1116
pgstat_drop_relation(Oid relid)
 
1117
{
 
1118
        PgStat_MsgTabpurge msg;
 
1119
        int                     len;
 
1120
 
 
1121
        if (pgStatSock == PGINVALID_SOCKET)
 
1122
                return;
 
1123
 
 
1124
        msg.m_tableid[0] = relid;
 
1125
        msg.m_nentries = 1;
 
1126
 
 
1127
        len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) +sizeof(Oid);
 
1128
 
 
1129
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE);
 
1130
        msg.m_databaseid = MyDatabaseId;
 
1131
        pgstat_send(&msg, len);
 
1132
}
 
1133
#endif   /* NOT_USED */
 
1134
 
 
1135
 
 
1136
/* ----------
 
1137
 * pgstat_reset_counters() -
 
1138
 *
 
1139
 *      Tell the statistics collector to reset counters for our database.
 
1140
 * ----------
 
1141
 */
 
1142
void
 
1143
pgstat_reset_counters(void)
 
1144
{
 
1145
        PgStat_MsgResetcounter msg;
 
1146
 
 
1147
        if (pgStatSock == PGINVALID_SOCKET)
 
1148
                return;
 
1149
 
 
1150
        if (!superuser())
 
1151
                ereport(ERROR,
 
1152
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
1153
                                 errmsg("must be superuser to reset statistics counters")));
 
1154
 
 
1155
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETCOUNTER);
 
1156
        msg.m_databaseid = MyDatabaseId;
 
1157
        pgstat_send(&msg, sizeof(msg));
 
1158
}
 
1159
 
 
1160
/* ----------
 
1161
 * pgstat_reset_shared_counters() -
 
1162
 *
 
1163
 *      Tell the statistics collector to reset cluster-wide shared counters.
 
1164
 * ----------
 
1165
 */
 
1166
void
 
1167
pgstat_reset_shared_counters(const char *target)
 
1168
{
 
1169
        PgStat_MsgResetsharedcounter msg;
 
1170
 
 
1171
        if (pgStatSock == PGINVALID_SOCKET)
 
1172
                return;
 
1173
 
 
1174
        if (!superuser())
 
1175
                ereport(ERROR,
 
1176
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
1177
                                 errmsg("must be superuser to reset statistics counters")));
 
1178
 
 
1179
        if (strcmp(target, "bgwriter") == 0)
 
1180
                msg.m_resettarget = RESET_BGWRITER;
 
1181
        else
 
1182
                ereport(ERROR,
 
1183
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 
1184
                                 errmsg("unrecognized reset target: \"%s\"", target),
 
1185
                                 errhint("Target must be \"bgwriter\".")));
 
1186
 
 
1187
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 
1188
        pgstat_send(&msg, sizeof(msg));
 
1189
}
 
1190
 
 
1191
/* ----------
 
1192
 * pgstat_reset_single_counter() -
 
1193
 *
 
1194
 *      Tell the statistics collector to reset a single counter.
 
1195
 * ----------
 
1196
 */
 
1197
void
 
1198
pgstat_reset_single_counter(Oid objoid, PgStat_Single_Reset_Type type)
 
1199
{
 
1200
        PgStat_MsgResetsinglecounter msg;
 
1201
 
 
1202
        if (pgStatSock == PGINVALID_SOCKET)
 
1203
                return;
 
1204
 
 
1205
        if (!superuser())
 
1206
                ereport(ERROR,
 
1207
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
1208
                                 errmsg("must be superuser to reset statistics counters")));
 
1209
 
 
1210
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSINGLECOUNTER);
 
1211
        msg.m_databaseid = MyDatabaseId;
 
1212
        msg.m_resettype = type;
 
1213
        msg.m_objectid = objoid;
 
1214
 
 
1215
        pgstat_send(&msg, sizeof(msg));
 
1216
}
 
1217
 
 
1218
/* ----------
 
1219
 * pgstat_report_autovac() -
 
1220
 *
 
1221
 *      Called from autovacuum.c to report startup of an autovacuum process.
 
1222
 *      We are called before InitPostgres is done, so can't rely on MyDatabaseId;
 
1223
 *      the db OID must be passed in, instead.
 
1224
 * ----------
 
1225
 */
 
1226
void
 
1227
pgstat_report_autovac(Oid dboid)
 
1228
{
 
1229
        PgStat_MsgAutovacStart msg;
 
1230
 
 
1231
        if (pgStatSock == PGINVALID_SOCKET)
 
1232
                return;
 
1233
 
 
1234
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_AUTOVAC_START);
 
1235
        msg.m_databaseid = dboid;
 
1236
        msg.m_start_time = GetCurrentTimestamp();
 
1237
 
 
1238
        pgstat_send(&msg, sizeof(msg));
 
1239
}
 
1240
 
 
1241
 
 
1242
/* ---------
 
1243
 * pgstat_report_vacuum() -
 
1244
 *
 
1245
 *      Tell the collector about the table we just vacuumed.
 
1246
 * ---------
 
1247
 */
 
1248
void
 
1249
pgstat_report_vacuum(Oid tableoid, bool shared, bool adopt_counts,
 
1250
                                         PgStat_Counter tuples)
 
1251
{
 
1252
        PgStat_MsgVacuum msg;
 
1253
 
 
1254
        if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
 
1255
                return;
 
1256
 
 
1257
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM);
 
1258
        msg.m_databaseid = shared ? InvalidOid : MyDatabaseId;
 
1259
        msg.m_tableoid = tableoid;
 
1260
        msg.m_adopt_counts = adopt_counts;
 
1261
        msg.m_autovacuum = IsAutoVacuumWorkerProcess();
 
1262
        msg.m_vacuumtime = GetCurrentTimestamp();
 
1263
        msg.m_tuples = tuples;
 
1264
        pgstat_send(&msg, sizeof(msg));
 
1265
}
 
1266
 
 
1267
/* --------
 
1268
 * pgstat_report_analyze() -
 
1269
 *
 
1270
 *      Tell the collector about the table we just analyzed.
 
1271
 * --------
 
1272
 */
 
1273
void
 
1274
pgstat_report_analyze(Relation rel, bool adopt_counts,
 
1275
                                          PgStat_Counter livetuples, PgStat_Counter deadtuples)
 
1276
{
 
1277
        PgStat_MsgAnalyze msg;
 
1278
 
 
1279
        if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
 
1280
                return;
 
1281
 
 
1282
        /*
 
1283
         * Unlike VACUUM, ANALYZE might be running inside a transaction that has
 
1284
         * already inserted and/or deleted rows in the target table. ANALYZE will
 
1285
         * have counted such rows as live or dead respectively. Because we will
 
1286
         * report our counts of such rows at transaction end, we should subtract
 
1287
         * off these counts from what we send to the collector now, else they'll
 
1288
         * be double-counted after commit.      (This approach also ensures that the
 
1289
         * collector ends up with the right numbers if we abort instead of
 
1290
         * committing.)
 
1291
         */
 
1292
        if (rel->pgstat_info != NULL)
 
1293
        {
 
1294
                PgStat_TableXactStatus *trans;
 
1295
 
 
1296
                for (trans = rel->pgstat_info->trans; trans; trans = trans->upper)
 
1297
                {
 
1298
                        livetuples -= trans->tuples_inserted - trans->tuples_deleted;
 
1299
                        deadtuples -= trans->tuples_updated + trans->tuples_deleted;
 
1300
                }
 
1301
                /* count stuff inserted by already-aborted subxacts, too */
 
1302
                deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
 
1303
                /* Since ANALYZE's counts are estimates, we could have underflowed */
 
1304
                livetuples = Max(livetuples, 0);
 
1305
                deadtuples = Max(deadtuples, 0);
 
1306
        }
 
1307
 
 
1308
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE);
 
1309
        msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
 
1310
        msg.m_tableoid = RelationGetRelid(rel);
 
1311
        msg.m_adopt_counts = adopt_counts;
 
1312
        msg.m_autovacuum = IsAutoVacuumWorkerProcess();
 
1313
        msg.m_analyzetime = GetCurrentTimestamp();
 
1314
        msg.m_live_tuples = livetuples;
 
1315
        msg.m_dead_tuples = deadtuples;
 
1316
        pgstat_send(&msg, sizeof(msg));
 
1317
}
 
1318
 
 
1319
/* --------
 
1320
 * pgstat_report_recovery_conflict() -
 
1321
 *
 
1322
 *      Tell the collector about a Hot Standby recovery conflict.
 
1323
 * --------
 
1324
 */
 
1325
void
 
1326
pgstat_report_recovery_conflict(int reason)
 
1327
{
 
1328
        PgStat_MsgRecoveryConflict msg;
 
1329
 
 
1330
        if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
 
1331
                return;
 
1332
 
 
1333
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RECOVERYCONFLICT);
 
1334
        msg.m_databaseid = MyDatabaseId;
 
1335
        msg.m_reason = reason;
 
1336
        pgstat_send(&msg, sizeof(msg));
 
1337
}
 
1338
 
 
1339
/* ----------
 
1340
 * pgstat_ping() -
 
1341
 *
 
1342
 *      Send some junk data to the collector to increase traffic.
 
1343
 * ----------
 
1344
 */
 
1345
void
 
1346
pgstat_ping(void)
 
1347
{
 
1348
        PgStat_MsgDummy msg;
 
1349
 
 
1350
        if (pgStatSock == PGINVALID_SOCKET)
 
1351
                return;
 
1352
 
 
1353
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DUMMY);
 
1354
        pgstat_send(&msg, sizeof(msg));
 
1355
}
 
1356
 
 
1357
/* ----------
 
1358
 * pgstat_send_inquiry() -
 
1359
 *
 
1360
 *      Notify collector that we need fresh data.
 
1361
 *      ts specifies the minimum acceptable timestamp for the stats file.
 
1362
 * ----------
 
1363
 */
 
1364
static void
 
1365
pgstat_send_inquiry(TimestampTz ts)
 
1366
{
 
1367
        PgStat_MsgInquiry msg;
 
1368
 
 
1369
        pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_INQUIRY);
 
1370
        msg.inquiry_time = ts;
 
1371
        pgstat_send(&msg, sizeof(msg));
 
1372
}
 
1373
 
 
1374
 
 
1375
/*
 
1376
 * Initialize function call usage data.
 
1377
 * Called by the executor before invoking a function.
 
1378
 */
 
1379
void
 
1380
pgstat_init_function_usage(FunctionCallInfoData *fcinfo,
 
1381
                                                   PgStat_FunctionCallUsage *fcu)
 
1382
{
 
1383
        PgStat_BackendFunctionEntry *htabent;
 
1384
        bool            found;
 
1385
 
 
1386
        if (pgstat_track_functions <= fcinfo->flinfo->fn_stats)
 
1387
        {
 
1388
                /* stats not wanted */
 
1389
                fcu->fs = NULL;
 
1390
                return;
 
1391
        }
 
1392
 
 
1393
        if (!pgStatFunctions)
 
1394
        {
 
1395
                /* First time through - initialize function stat table */
 
1396
                HASHCTL         hash_ctl;
 
1397
 
 
1398
                memset(&hash_ctl, 0, sizeof(hash_ctl));
 
1399
                hash_ctl.keysize = sizeof(Oid);
 
1400
                hash_ctl.entrysize = sizeof(PgStat_BackendFunctionEntry);
 
1401
                hash_ctl.hash = oid_hash;
 
1402
                pgStatFunctions = hash_create("Function stat entries",
 
1403
                                                                          PGSTAT_FUNCTION_HASH_SIZE,
 
1404
                                                                          &hash_ctl,
 
1405
                                                                          HASH_ELEM | HASH_FUNCTION);
 
1406
        }
 
1407
 
 
1408
        /* Get the stats entry for this function, create if necessary */
 
1409
        htabent = hash_search(pgStatFunctions, &fcinfo->flinfo->fn_oid,
 
1410
                                                  HASH_ENTER, &found);
 
1411
        if (!found)
 
1412
                MemSet(&htabent->f_counts, 0, sizeof(PgStat_FunctionCounts));
 
1413
 
 
1414
        fcu->fs = &htabent->f_counts;
 
1415
 
 
1416
        /* save stats for this function, later used to compensate for recursion */
 
1417
        fcu->save_f_time = htabent->f_counts.f_time;
 
1418
 
 
1419
        /* save current backend-wide total time */
 
1420
        fcu->save_total = total_func_time;
 
1421
 
 
1422
        /* get clock time as of function start */
 
1423
        INSTR_TIME_SET_CURRENT(fcu->f_start);
 
1424
}
 
1425
 
 
1426
/*
 
1427
 * find_funcstat_entry - find any existing PgStat_BackendFunctionEntry entry
 
1428
 *              for specified function
 
1429
 *
 
1430
 * If no entry, return NULL, don't create a new one
 
1431
 */
 
1432
PgStat_BackendFunctionEntry *
 
1433
find_funcstat_entry(Oid func_id)
 
1434
{
 
1435
        if (pgStatFunctions == NULL)
 
1436
                return NULL;
 
1437
 
 
1438
        return (PgStat_BackendFunctionEntry *) hash_search(pgStatFunctions,
 
1439
                                                                                                           (void *) &func_id,
 
1440
                                                                                                           HASH_FIND, NULL);
 
1441
}
 
1442
 
 
1443
/*
 
1444
 * Calculate function call usage and update stat counters.
 
1445
 * Called by the executor after invoking a function.
 
1446
 *
 
1447
 * In the case of a set-returning function that runs in value-per-call mode,
 
1448
 * we will see multiple pgstat_init_function_usage/pgstat_end_function_usage
 
1449
 * calls for what the user considers a single call of the function.  The
 
1450
 * finalize flag should be TRUE on the last call.
 
1451
 */
 
1452
void
 
1453
pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
 
1454
{
 
1455
        PgStat_FunctionCounts *fs = fcu->fs;
 
1456
        instr_time      f_total;
 
1457
        instr_time      f_others;
 
1458
        instr_time      f_self;
 
1459
 
 
1460
        /* stats not wanted? */
 
1461
        if (fs == NULL)
 
1462
                return;
 
1463
 
 
1464
        /* total elapsed time in this function call */
 
1465
        INSTR_TIME_SET_CURRENT(f_total);
 
1466
        INSTR_TIME_SUBTRACT(f_total, fcu->f_start);
 
1467
 
 
1468
        /* self usage: elapsed minus anything already charged to other calls */
 
1469
        f_others = total_func_time;
 
1470
        INSTR_TIME_SUBTRACT(f_others, fcu->save_total);
 
1471
        f_self = f_total;
 
1472
        INSTR_TIME_SUBTRACT(f_self, f_others);
 
1473
 
 
1474
        /* update backend-wide total time */
 
1475
        INSTR_TIME_ADD(total_func_time, f_self);
 
1476
 
 
1477
        /*
 
1478
         * Compute the new total f_time as the total elapsed time added to the
 
1479
         * pre-call value of f_time.  This is necessary to avoid double-counting
 
1480
         * any time taken by recursive calls of myself.  (We do not need any
 
1481
         * similar kluge for self time, since that already excludes any recursive
 
1482
         * calls.)
 
1483
         */
 
1484
        INSTR_TIME_ADD(f_total, fcu->save_f_time);
 
1485
 
 
1486
        /* update counters in function stats table */
 
1487
        if (finalize)
 
1488
                fs->f_numcalls++;
 
1489
        fs->f_time = f_total;
 
1490
        INSTR_TIME_ADD(fs->f_time_self, f_self);
 
1491
 
 
1492
        /* indicate that we have something to send */
 
1493
        have_function_stats = true;
 
1494
}
 
1495
 
 
1496
 
 
1497
/* ----------
 
1498
 * pgstat_initstats() -
 
1499
 *
 
1500
 *      Initialize a relcache entry to count access statistics.
 
1501
 *      Called whenever a relation is opened.
 
1502
 *
 
1503
 *      We assume that a relcache entry's pgstat_info field is zeroed by
 
1504
 *      relcache.c when the relcache entry is made; thereafter it is long-lived
 
1505
 *      data.  We can avoid repeated searches of the TabStatus arrays when the
 
1506
 *      same relation is touched repeatedly within a transaction.
 
1507
 * ----------
 
1508
 */
 
1509
void
 
1510
pgstat_initstats(Relation rel)
 
1511
{
 
1512
        Oid                     rel_id = rel->rd_id;
 
1513
        char            relkind = rel->rd_rel->relkind;
 
1514
 
 
1515
        /* We only count stats for things that have storage */
 
1516
        if (!(relkind == RELKIND_RELATION ||
 
1517
                  relkind == RELKIND_INDEX ||
 
1518
                  relkind == RELKIND_TOASTVALUE ||
 
1519
                  relkind == RELKIND_SEQUENCE))
 
1520
        {
 
1521
                rel->pgstat_info = NULL;
 
1522
                return;
 
1523
        }
 
1524
 
 
1525
        if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
 
1526
        {
 
1527
                /* We're not counting at all */
 
1528
                rel->pgstat_info = NULL;
 
1529
                return;
 
1530
        }
 
1531
 
 
1532
        /*
 
1533
         * If we already set up this relation in the current transaction, nothing
 
1534
         * to do.
 
1535
         */
 
1536
        if (rel->pgstat_info != NULL &&
 
1537
                rel->pgstat_info->t_id == rel_id)
 
1538
                return;
 
1539
 
 
1540
        /* Else find or make the PgStat_TableStatus entry, and update link */
 
1541
        rel->pgstat_info = get_tabstat_entry(rel_id, rel->rd_rel->relisshared);
 
1542
}
 
1543
 
 
1544
/*
 
1545
 * get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
 
1546
 */
 
1547
static PgStat_TableStatus *
 
1548
get_tabstat_entry(Oid rel_id, bool isshared)
 
1549
{
 
1550
        PgStat_TableStatus *entry;
 
1551
        TabStatusArray *tsa;
 
1552
        TabStatusArray *prev_tsa;
 
1553
        int                     i;
 
1554
 
 
1555
        /*
 
1556
         * Search the already-used tabstat slots for this relation.
 
1557
         */
 
1558
        prev_tsa = NULL;
 
1559
        for (tsa = pgStatTabList; tsa != NULL; prev_tsa = tsa, tsa = tsa->tsa_next)
 
1560
        {
 
1561
                for (i = 0; i < tsa->tsa_used; i++)
 
1562
                {
 
1563
                        entry = &tsa->tsa_entries[i];
 
1564
                        if (entry->t_id == rel_id)
 
1565
                                return entry;
 
1566
                }
 
1567
 
 
1568
                if (tsa->tsa_used < TABSTAT_QUANTUM)
 
1569
                {
 
1570
                        /*
 
1571
                         * It must not be present, but we found a free slot instead. Fine,
 
1572
                         * let's use this one.  We assume the entry was already zeroed,
 
1573
                         * either at creation or after last use.
 
1574
                         */
 
1575
                        entry = &tsa->tsa_entries[tsa->tsa_used++];
 
1576
                        entry->t_id = rel_id;
 
1577
                        entry->t_shared = isshared;
 
1578
                        return entry;
 
1579
                }
 
1580
        }
 
1581
 
 
1582
        /*
 
1583
         * We ran out of tabstat slots, so allocate more.  Be sure they're zeroed.
 
1584
         */
 
1585
        tsa = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
 
1586
                                                                                                        sizeof(TabStatusArray));
 
1587
        if (prev_tsa)
 
1588
                prev_tsa->tsa_next = tsa;
 
1589
        else
 
1590
                pgStatTabList = tsa;
 
1591
 
 
1592
        /*
 
1593
         * Use the first entry of the new TabStatusArray.
 
1594
         */
 
1595
        entry = &tsa->tsa_entries[tsa->tsa_used++];
 
1596
        entry->t_id = rel_id;
 
1597
        entry->t_shared = isshared;
 
1598
        return entry;
 
1599
}
 
1600
 
 
1601
/*
 
1602
 * find_tabstat_entry - find any existing PgStat_TableStatus entry for rel
 
1603
 *
 
1604
 * If no entry, return NULL, don't create a new one
 
1605
 */
 
1606
PgStat_TableStatus *
 
1607
find_tabstat_entry(Oid rel_id)
 
1608
{
 
1609
        PgStat_TableStatus *entry;
 
1610
        TabStatusArray *tsa;
 
1611
        int                     i;
 
1612
 
 
1613
        for (tsa = pgStatTabList; tsa != NULL; tsa = tsa->tsa_next)
 
1614
        {
 
1615
                for (i = 0; i < tsa->tsa_used; i++)
 
1616
                {
 
1617
                        entry = &tsa->tsa_entries[i];
 
1618
                        if (entry->t_id == rel_id)
 
1619
                                return entry;
 
1620
                }
 
1621
        }
 
1622
 
 
1623
        /* Not present */
 
1624
        return NULL;
 
1625
}
 
1626
 
 
1627
/*
 
1628
 * get_tabstat_stack_level - add a new (sub)transaction stack entry if needed
 
1629
 */
 
1630
static PgStat_SubXactStatus *
 
1631
get_tabstat_stack_level(int nest_level)
 
1632
{
 
1633
        PgStat_SubXactStatus *xact_state;
 
1634
 
 
1635
        xact_state = pgStatXactStack;
 
1636
        if (xact_state == NULL || xact_state->nest_level != nest_level)
 
1637
        {
 
1638
                xact_state = (PgStat_SubXactStatus *)
 
1639
                        MemoryContextAlloc(TopTransactionContext,
 
1640
                                                           sizeof(PgStat_SubXactStatus));
 
1641
                xact_state->nest_level = nest_level;
 
1642
                xact_state->prev = pgStatXactStack;
 
1643
                xact_state->first = NULL;
 
1644
                pgStatXactStack = xact_state;
 
1645
        }
 
1646
        return xact_state;
 
1647
}
 
1648
 
 
1649
/*
 
1650
 * add_tabstat_xact_level - add a new (sub)transaction state record
 
1651
 */
 
1652
static void
 
1653
add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level)
 
1654
{
 
1655
        PgStat_SubXactStatus *xact_state;
 
1656
        PgStat_TableXactStatus *trans;
 
1657
 
 
1658
        /*
 
1659
         * If this is the first rel to be modified at the current nest level, we
 
1660
         * first have to push a transaction stack entry.
 
1661
         */
 
1662
        xact_state = get_tabstat_stack_level(nest_level);
 
1663
 
 
1664
        /* Now make a per-table stack entry */
 
1665
        trans = (PgStat_TableXactStatus *)
 
1666
                MemoryContextAllocZero(TopTransactionContext,
 
1667
                                                           sizeof(PgStat_TableXactStatus));
 
1668
        trans->nest_level = nest_level;
 
1669
        trans->upper = pgstat_info->trans;
 
1670
        trans->parent = pgstat_info;
 
1671
        trans->next = xact_state->first;
 
1672
        xact_state->first = trans;
 
1673
        pgstat_info->trans = trans;
 
1674
}
 
1675
 
 
1676
/*
 
1677
 * pgstat_count_heap_insert - count a tuple insertion
 
1678
 */
 
1679
void
 
1680
pgstat_count_heap_insert(Relation rel)
 
1681
{
 
1682
        PgStat_TableStatus *pgstat_info = rel->pgstat_info;
 
1683
 
 
1684
        if (pgstat_info != NULL)
 
1685
        {
 
1686
                /* We have to log the effect at the proper transactional level */
 
1687
                int                     nest_level = GetCurrentTransactionNestLevel();
 
1688
 
 
1689
                if (pgstat_info->trans == NULL ||
 
1690
                        pgstat_info->trans->nest_level != nest_level)
 
1691
                        add_tabstat_xact_level(pgstat_info, nest_level);
 
1692
 
 
1693
                pgstat_info->trans->tuples_inserted++;
 
1694
        }
 
1695
}
 
1696
 
 
1697
/*
 
1698
 * pgstat_count_heap_update - count a tuple update
 
1699
 */
 
1700
void
 
1701
pgstat_count_heap_update(Relation rel, bool hot)
 
1702
{
 
1703
        PgStat_TableStatus *pgstat_info = rel->pgstat_info;
 
1704
 
 
1705
        if (pgstat_info != NULL)
 
1706
        {
 
1707
                /* We have to log the effect at the proper transactional level */
 
1708
                int                     nest_level = GetCurrentTransactionNestLevel();
 
1709
 
 
1710
                if (pgstat_info->trans == NULL ||
 
1711
                        pgstat_info->trans->nest_level != nest_level)
 
1712
                        add_tabstat_xact_level(pgstat_info, nest_level);
 
1713
 
 
1714
                pgstat_info->trans->tuples_updated++;
 
1715
 
 
1716
                /* t_tuples_hot_updated is nontransactional, so just advance it */
 
1717
                if (hot)
 
1718
                        pgstat_info->t_counts.t_tuples_hot_updated++;
 
1719
        }
 
1720
}
 
1721
 
 
1722
/*
 
1723
 * pgstat_count_heap_delete - count a tuple deletion
 
1724
 */
 
1725
void
 
1726
pgstat_count_heap_delete(Relation rel)
 
1727
{
 
1728
        PgStat_TableStatus *pgstat_info = rel->pgstat_info;
 
1729
 
 
1730
        if (pgstat_info != NULL)
 
1731
        {
 
1732
                /* We have to log the effect at the proper transactional level */
 
1733
                int                     nest_level = GetCurrentTransactionNestLevel();
 
1734
 
 
1735
                if (pgstat_info->trans == NULL ||
 
1736
                        pgstat_info->trans->nest_level != nest_level)
 
1737
                        add_tabstat_xact_level(pgstat_info, nest_level);
 
1738
 
 
1739
                pgstat_info->trans->tuples_deleted++;
 
1740
        }
 
1741
}
 
1742
 
 
1743
/*
 
1744
 * pgstat_update_heap_dead_tuples - update dead-tuples count
 
1745
 *
 
1746
 * The semantics of this are that we are reporting the nontransactional
 
1747
 * recovery of "delta" dead tuples; so t_delta_dead_tuples decreases
 
1748
 * rather than increasing, and the change goes straight into the per-table
 
1749
 * counter, not into transactional state.
 
1750
 */
 
1751
void
 
1752
pgstat_update_heap_dead_tuples(Relation rel, int delta)
 
1753
{
 
1754
        PgStat_TableStatus *pgstat_info = rel->pgstat_info;
 
1755
 
 
1756
        if (pgstat_info != NULL)
 
1757
                pgstat_info->t_counts.t_delta_dead_tuples -= delta;
 
1758
}
 
1759
 
 
1760
 
 
1761
/* ----------
 
1762
 * AtEOXact_PgStat
 
1763
 *
 
1764
 *      Called from access/transam/xact.c at top-level transaction commit/abort.
 
1765
 * ----------
 
1766
 */
 
1767
void
 
1768
AtEOXact_PgStat(bool isCommit)
 
1769
{
 
1770
        PgStat_SubXactStatus *xact_state;
 
1771
 
 
1772
        /*
 
1773
         * Count transaction commit or abort.  (We use counters, not just bools,
 
1774
         * in case the reporting message isn't sent right away.)
 
1775
         */
 
1776
        if (isCommit)
 
1777
                pgStatXactCommit++;
 
1778
        else
 
1779
                pgStatXactRollback++;
 
1780
 
 
1781
        /*
 
1782
         * Transfer transactional insert/update counts into the base tabstat
 
1783
         * entries.  We don't bother to free any of the transactional state, since
 
1784
         * it's all in TopTransactionContext and will go away anyway.
 
1785
         */
 
1786
        xact_state = pgStatXactStack;
 
1787
        if (xact_state != NULL)
 
1788
        {
 
1789
                PgStat_TableXactStatus *trans;
 
1790
 
 
1791
                Assert(xact_state->nest_level == 1);
 
1792
                Assert(xact_state->prev == NULL);
 
1793
                for (trans = xact_state->first; trans != NULL; trans = trans->next)
 
1794
                {
 
1795
                        PgStat_TableStatus *tabstat;
 
1796
 
 
1797
                        Assert(trans->nest_level == 1);
 
1798
                        Assert(trans->upper == NULL);
 
1799
                        tabstat = trans->parent;
 
1800
                        Assert(tabstat->trans == trans);
 
1801
                        /* count attempted actions regardless of commit/abort */
 
1802
                        tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
 
1803
                        tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
 
1804
                        tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
 
1805
                        if (isCommit)
 
1806
                        {
 
1807
                                /* insert adds a live tuple, delete removes one */
 
1808
                                tabstat->t_counts.t_delta_live_tuples +=
 
1809
                                        trans->tuples_inserted - trans->tuples_deleted;
 
1810
                                /* update and delete each create a dead tuple */
 
1811
                                tabstat->t_counts.t_delta_dead_tuples +=
 
1812
                                        trans->tuples_updated + trans->tuples_deleted;
 
1813
                                /* insert, update, delete each count as one change event */
 
1814
                                tabstat->t_counts.t_changed_tuples +=
 
1815
                                        trans->tuples_inserted + trans->tuples_updated +
 
1816
                                        trans->tuples_deleted;
 
1817
                        }
 
1818
                        else
 
1819
                        {
 
1820
                                /* inserted tuples are dead, deleted tuples are unaffected */
 
1821
                                tabstat->t_counts.t_delta_dead_tuples +=
 
1822
                                        trans->tuples_inserted + trans->tuples_updated;
 
1823
                                /* an aborted xact generates no changed_tuple events */
 
1824
                        }
 
1825
                        tabstat->trans = NULL;
 
1826
                }
 
1827
        }
 
1828
        pgStatXactStack = NULL;
 
1829
 
 
1830
        /* Make sure any stats snapshot is thrown away */
 
1831
        pgstat_clear_snapshot();
 
1832
}
 
1833
 
 
1834
/* ----------
 
1835
 * AtEOSubXact_PgStat
 
1836
 *
 
1837
 *      Called from access/transam/xact.c at subtransaction commit/abort.
 
1838
 * ----------
 
1839
 */
 
1840
void
 
1841
AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 
1842
{
 
1843
        PgStat_SubXactStatus *xact_state;
 
1844
 
 
1845
        /*
 
1846
         * Transfer transactional insert/update counts into the next higher
 
1847
         * subtransaction state.
 
1848
         */
 
1849
        xact_state = pgStatXactStack;
 
1850
        if (xact_state != NULL &&
 
1851
                xact_state->nest_level >= nestDepth)
 
1852
        {
 
1853
                PgStat_TableXactStatus *trans;
 
1854
                PgStat_TableXactStatus *next_trans;
 
1855
 
 
1856
                /* delink xact_state from stack immediately to simplify reuse case */
 
1857
                pgStatXactStack = xact_state->prev;
 
1858
 
 
1859
                for (trans = xact_state->first; trans != NULL; trans = next_trans)
 
1860
                {
 
1861
                        PgStat_TableStatus *tabstat;
 
1862
 
 
1863
                        next_trans = trans->next;
 
1864
                        Assert(trans->nest_level == nestDepth);
 
1865
                        tabstat = trans->parent;
 
1866
                        Assert(tabstat->trans == trans);
 
1867
                        if (isCommit)
 
1868
                        {
 
1869
                                if (trans->upper && trans->upper->nest_level == nestDepth - 1)
 
1870
                                {
 
1871
                                        trans->upper->tuples_inserted += trans->tuples_inserted;
 
1872
                                        trans->upper->tuples_updated += trans->tuples_updated;
 
1873
                                        trans->upper->tuples_deleted += trans->tuples_deleted;
 
1874
                                        tabstat->trans = trans->upper;
 
1875
                                        pfree(trans);
 
1876
                                }
 
1877
                                else
 
1878
                                {
 
1879
                                        /*
 
1880
                                         * When there isn't an immediate parent state, we can just
 
1881
                                         * reuse the record instead of going through a
 
1882
                                         * palloc/pfree pushup (this works since it's all in
 
1883
                                         * TopTransactionContext anyway).  We have to re-link it
 
1884
                                         * into the parent level, though, and that might mean
 
1885
                                         * pushing a new entry into the pgStatXactStack.
 
1886
                                         */
 
1887
                                        PgStat_SubXactStatus *upper_xact_state;
 
1888
 
 
1889
                                        upper_xact_state = get_tabstat_stack_level(nestDepth - 1);
 
1890
                                        trans->next = upper_xact_state->first;
 
1891
                                        upper_xact_state->first = trans;
 
1892
                                        trans->nest_level = nestDepth - 1;
 
1893
                                }
 
1894
                        }
 
1895
                        else
 
1896
                        {
 
1897
                                /*
 
1898
                                 * On abort, update top-level tabstat counts, then forget the
 
1899
                                 * subtransaction
 
1900
                                 */
 
1901
 
 
1902
                                /* count attempted actions regardless of commit/abort */
 
1903
                                tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
 
1904
                                tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
 
1905
                                tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
 
1906
                                /* inserted tuples are dead, deleted tuples are unaffected */
 
1907
                                tabstat->t_counts.t_delta_dead_tuples +=
 
1908
                                        trans->tuples_inserted + trans->tuples_updated;
 
1909
                                tabstat->trans = trans->upper;
 
1910
                                pfree(trans);
 
1911
                        }
 
1912
                }
 
1913
                pfree(xact_state);
 
1914
        }
 
1915
}
 
1916
 
 
1917
 
 
1918
/*
 
1919
 * AtPrepare_PgStat
 
1920
 *              Save the transactional stats state at 2PC transaction prepare.
 
1921
 *
 
1922
 * In this phase we just generate 2PC records for all the pending
 
1923
 * transaction-dependent stats work.
 
1924
 */
 
1925
void
 
1926
AtPrepare_PgStat(void)
 
1927
{
 
1928
        PgStat_SubXactStatus *xact_state;
 
1929
 
 
1930
        xact_state = pgStatXactStack;
 
1931
        if (xact_state != NULL)
 
1932
        {
 
1933
                PgStat_TableXactStatus *trans;
 
1934
 
 
1935
                Assert(xact_state->nest_level == 1);
 
1936
                Assert(xact_state->prev == NULL);
 
1937
                for (trans = xact_state->first; trans != NULL; trans = trans->next)
 
1938
                {
 
1939
                        PgStat_TableStatus *tabstat;
 
1940
                        TwoPhasePgStatRecord record;
 
1941
 
 
1942
                        Assert(trans->nest_level == 1);
 
1943
                        Assert(trans->upper == NULL);
 
1944
                        tabstat = trans->parent;
 
1945
                        Assert(tabstat->trans == trans);
 
1946
 
 
1947
                        record.tuples_inserted = trans->tuples_inserted;
 
1948
                        record.tuples_updated = trans->tuples_updated;
 
1949
                        record.tuples_deleted = trans->tuples_deleted;
 
1950
                        record.t_id = tabstat->t_id;
 
1951
                        record.t_shared = tabstat->t_shared;
 
1952
 
 
1953
                        RegisterTwoPhaseRecord(TWOPHASE_RM_PGSTAT_ID, 0,
 
1954
                                                                   &record, sizeof(TwoPhasePgStatRecord));
 
1955
                }
 
1956
        }
 
1957
}
 
1958
 
 
1959
/*
 
1960
 * PostPrepare_PgStat
 
1961
 *              Clean up after successful PREPARE.
 
1962
 *
 
1963
 * All we need do here is unlink the transaction stats state from the
 
1964
 * nontransactional state.      The nontransactional action counts will be
 
1965
 * reported to the stats collector immediately, while the effects on live
 
1966
 * and dead tuple counts are preserved in the 2PC state file.
 
1967
 *
 
1968
 * Note: AtEOXact_PgStat is not called during PREPARE.
 
1969
 */
 
1970
void
 
1971
PostPrepare_PgStat(void)
 
1972
{
 
1973
        PgStat_SubXactStatus *xact_state;
 
1974
 
 
1975
        /*
 
1976
         * We don't bother to free any of the transactional state, since it's all
 
1977
         * in TopTransactionContext and will go away anyway.
 
1978
         */
 
1979
        xact_state = pgStatXactStack;
 
1980
        if (xact_state != NULL)
 
1981
        {
 
1982
                PgStat_TableXactStatus *trans;
 
1983
 
 
1984
                for (trans = xact_state->first; trans != NULL; trans = trans->next)
 
1985
                {
 
1986
                        PgStat_TableStatus *tabstat;
 
1987
 
 
1988
                        tabstat = trans->parent;
 
1989
                        tabstat->trans = NULL;
 
1990
                }
 
1991
        }
 
1992
        pgStatXactStack = NULL;
 
1993
 
 
1994
        /* Make sure any stats snapshot is thrown away */
 
1995
        pgstat_clear_snapshot();
 
1996
}
 
1997
 
 
1998
/*
 
1999
 * 2PC processing routine for COMMIT PREPARED case.
 
2000
 *
 
2001
 * Load the saved counts into our local pgstats state.
 
2002
 */
 
2003
void
 
2004
pgstat_twophase_postcommit(TransactionId xid, uint16 info,
 
2005
                                                   void *recdata, uint32 len)
 
2006
{
 
2007
        TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
 
2008
        PgStat_TableStatus *pgstat_info;
 
2009
 
 
2010
        /* Find or create a tabstat entry for the rel */
 
2011
        pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
 
2012
 
 
2013
        /* Same math as in AtEOXact_PgStat, commit case */
 
2014
        pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
 
2015
        pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
 
2016
        pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
 
2017
        pgstat_info->t_counts.t_delta_live_tuples +=
 
2018
                rec->tuples_inserted - rec->tuples_deleted;
 
2019
        pgstat_info->t_counts.t_delta_dead_tuples +=
 
2020
                rec->tuples_updated + rec->tuples_deleted;
 
2021
        pgstat_info->t_counts.t_changed_tuples +=
 
2022
                rec->tuples_inserted + rec->tuples_updated +
 
2023
                rec->tuples_deleted;
 
2024
}
 
2025
 
 
2026
/*
 
2027
 * 2PC processing routine for ROLLBACK PREPARED case.
 
2028
 *
 
2029
 * Load the saved counts into our local pgstats state, but treat them
 
2030
 * as aborted.
 
2031
 */
 
2032
void
 
2033
pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
2034
                                                  void *recdata, uint32 len)
 
2035
{
 
2036
        TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
 
2037
        PgStat_TableStatus *pgstat_info;
 
2038
 
 
2039
        /* Find or create a tabstat entry for the rel */
 
2040
        pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
 
2041
 
 
2042
        /* Same math as in AtEOXact_PgStat, abort case */
 
2043
        pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
 
2044
        pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
 
2045
        pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
 
2046
        pgstat_info->t_counts.t_delta_dead_tuples +=
 
2047
                rec->tuples_inserted + rec->tuples_updated;
 
2048
}
 
2049
 
 
2050
 
 
2051
/* ----------
 
2052
 * pgstat_fetch_stat_dbentry() -
 
2053
 *
 
2054
 *      Support function for the SQL-callable pgstat* functions. Returns
 
2055
 *      the collected statistics for one database or NULL. NULL doesn't mean
 
2056
 *      that the database doesn't exist, it is just not yet known by the
 
2057
 *      collector, so the caller is better off to report ZERO instead.
 
2058
 * ----------
 
2059
 */
 
2060
PgStat_StatDBEntry *
 
2061
pgstat_fetch_stat_dbentry(Oid dbid)
 
2062
{
 
2063
        /*
 
2064
         * If not done for this transaction, read the statistics collector stats
 
2065
         * file into some hash tables.
 
2066
         */
 
2067
        backend_read_statsfile();
 
2068
 
 
2069
        /*
 
2070
         * Lookup the requested database; return NULL if not found
 
2071
         */
 
2072
        return (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
 
2073
                                                                                          (void *) &dbid,
 
2074
                                                                                          HASH_FIND, NULL);
 
2075
}
 
2076
 
 
2077
 
 
2078
/* ----------
 
2079
 * pgstat_fetch_stat_tabentry() -
 
2080
 *
 
2081
 *      Support function for the SQL-callable pgstat* functions. Returns
 
2082
 *      the collected statistics for one table or NULL. NULL doesn't mean
 
2083
 *      that the table doesn't exist, it is just not yet known by the
 
2084
 *      collector, so the caller is better off to report ZERO instead.
 
2085
 * ----------
 
2086
 */
 
2087
PgStat_StatTabEntry *
 
2088
pgstat_fetch_stat_tabentry(Oid relid)
 
2089
{
 
2090
        Oid                     dbid;
 
2091
        PgStat_StatDBEntry *dbentry;
 
2092
        PgStat_StatTabEntry *tabentry;
 
2093
 
 
2094
        /*
 
2095
         * If not done for this transaction, read the statistics collector stats
 
2096
         * file into some hash tables.
 
2097
         */
 
2098
        backend_read_statsfile();
 
2099
 
 
2100
        /*
 
2101
         * Lookup our database, then look in its table hash table.
 
2102
         */
 
2103
        dbid = MyDatabaseId;
 
2104
        dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
 
2105
                                                                                                 (void *) &dbid,
 
2106
                                                                                                 HASH_FIND, NULL);
 
2107
        if (dbentry != NULL && dbentry->tables != NULL)
 
2108
        {
 
2109
                tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
 
2110
                                                                                                           (void *) &relid,
 
2111
                                                                                                           HASH_FIND, NULL);
 
2112
                if (tabentry)
 
2113
                        return tabentry;
 
2114
        }
 
2115
 
 
2116
        /*
 
2117
         * If we didn't find it, maybe it's a shared table.
 
2118
         */
 
2119
        dbid = InvalidOid;
 
2120
        dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
 
2121
                                                                                                 (void *) &dbid,
 
2122
                                                                                                 HASH_FIND, NULL);
 
2123
        if (dbentry != NULL && dbentry->tables != NULL)
 
2124
        {
 
2125
                tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
 
2126
                                                                                                           (void *) &relid,
 
2127
                                                                                                           HASH_FIND, NULL);
 
2128
                if (tabentry)
 
2129
                        return tabentry;
 
2130
        }
 
2131
 
 
2132
        return NULL;
 
2133
}
 
2134
 
 
2135
 
 
2136
/* ----------
 
2137
 * pgstat_fetch_stat_funcentry() -
 
2138
 *
 
2139
 *      Support function for the SQL-callable pgstat* functions. Returns
 
2140
 *      the collected statistics for one function or NULL.
 
2141
 * ----------
 
2142
 */
 
2143
PgStat_StatFuncEntry *
 
2144
pgstat_fetch_stat_funcentry(Oid func_id)
 
2145
{
 
2146
        PgStat_StatDBEntry *dbentry;
 
2147
        PgStat_StatFuncEntry *funcentry = NULL;
 
2148
 
 
2149
        /* load the stats file if needed */
 
2150
        backend_read_statsfile();
 
2151
 
 
2152
        /* Lookup our database, then find the requested function.  */
 
2153
        dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
 
2154
        if (dbentry != NULL && dbentry->functions != NULL)
 
2155
        {
 
2156
                funcentry = (PgStat_StatFuncEntry *) hash_search(dbentry->functions,
 
2157
                                                                                                                 (void *) &func_id,
 
2158
                                                                                                                 HASH_FIND, NULL);
 
2159
        }
 
2160
 
 
2161
        return funcentry;
 
2162
}
 
2163
 
 
2164
 
 
2165
/* ----------
 
2166
 * pgstat_fetch_stat_beentry() -
 
2167
 *
 
2168
 *      Support function for the SQL-callable pgstat* functions. Returns
 
2169
 *      our local copy of the current-activity entry for one backend.
 
2170
 *
 
2171
 *      NB: caller is responsible for a check if the user is permitted to see
 
2172
 *      this info (especially the querystring).
 
2173
 * ----------
 
2174
 */
 
2175
PgBackendStatus *
 
2176
pgstat_fetch_stat_beentry(int beid)
 
2177
{
 
2178
        pgstat_read_current_status();
 
2179
 
 
2180
        if (beid < 1 || beid > localNumBackends)
 
2181
                return NULL;
 
2182
 
 
2183
        return &localBackendStatusTable[beid - 1];
 
2184
}
 
2185
 
 
2186
 
 
2187
/* ----------
 
2188
 * pgstat_fetch_stat_numbackends() -
 
2189
 *
 
2190
 *      Support function for the SQL-callable pgstat* functions. Returns
 
2191
 *      the maximum current backend id.
 
2192
 * ----------
 
2193
 */
 
2194
int
 
2195
pgstat_fetch_stat_numbackends(void)
 
2196
{
 
2197
        pgstat_read_current_status();
 
2198
 
 
2199
        return localNumBackends;
 
2200
}
 
2201
 
 
2202
/*
 
2203
 * ---------
 
2204
 * pgstat_fetch_global() -
 
2205
 *
 
2206
 *      Support function for the SQL-callable pgstat* functions. Returns
 
2207
 *      a pointer to the global statistics struct.
 
2208
 * ---------
 
2209
 */
 
2210
PgStat_GlobalStats *
 
2211
pgstat_fetch_global(void)
 
2212
{
 
2213
        backend_read_statsfile();
 
2214
 
 
2215
        return &globalStats;
 
2216
}
 
2217
 
 
2218
 
 
2219
/* ------------------------------------------------------------
 
2220
 * Functions for management of the shared-memory PgBackendStatus array
 
2221
 * ------------------------------------------------------------
 
2222
 */
 
2223
 
 
2224
static PgBackendStatus *BackendStatusArray = NULL;
 
2225
static PgBackendStatus *MyBEEntry = NULL;
 
2226
static char *BackendClientHostnameBuffer = NULL;
 
2227
static char *BackendAppnameBuffer = NULL;
 
2228
static char *BackendActivityBuffer = NULL;
 
2229
 
 
2230
 
 
2231
/*
 
2232
 * Report shared-memory space needed by CreateSharedBackendStatus.
 
2233
 */
 
2234
Size
 
2235
BackendStatusShmemSize(void)
 
2236
{
 
2237
        Size            size;
 
2238
 
 
2239
        size = mul_size(sizeof(PgBackendStatus), MaxBackends);
 
2240
        size = add_size(size,
 
2241
                                        mul_size(NAMEDATALEN, MaxBackends));
 
2242
        size = add_size(size,
 
2243
                                        mul_size(pgstat_track_activity_query_size, MaxBackends));
 
2244
        size = add_size(size,
 
2245
                                        mul_size(NAMEDATALEN, MaxBackends));
 
2246
        return size;
 
2247
}
 
2248
 
 
2249
/*
 
2250
 * Initialize the shared status array and several string buffers
 
2251
 * during postmaster startup.
 
2252
 */
 
2253
void
 
2254
CreateSharedBackendStatus(void)
 
2255
{
 
2256
        Size            size;
 
2257
        bool            found;
 
2258
        int                     i;
 
2259
        char       *buffer;
 
2260
 
 
2261
        /* Create or attach to the shared array */
 
2262
        size = mul_size(sizeof(PgBackendStatus), MaxBackends);
 
2263
        BackendStatusArray = (PgBackendStatus *)
 
2264
                ShmemInitStruct("Backend Status Array", size, &found);
 
2265
 
 
2266
        if (!found)
 
2267
        {
 
2268
                /*
 
2269
                 * We're the first - initialize.
 
2270
                 */
 
2271
                MemSet(BackendStatusArray, 0, size);
 
2272
        }
 
2273
 
 
2274
        /* Create or attach to the shared appname buffer */
 
2275
        size = mul_size(NAMEDATALEN, MaxBackends);
 
2276
        BackendAppnameBuffer = (char *)
 
2277
                ShmemInitStruct("Backend Application Name Buffer", size, &found);
 
2278
 
 
2279
        if (!found)
 
2280
        {
 
2281
                MemSet(BackendAppnameBuffer, 0, size);
 
2282
 
 
2283
                /* Initialize st_appname pointers. */
 
2284
                buffer = BackendAppnameBuffer;
 
2285
                for (i = 0; i < MaxBackends; i++)
 
2286
                {
 
2287
                        BackendStatusArray[i].st_appname = buffer;
 
2288
                        buffer += NAMEDATALEN;
 
2289
                }
 
2290
        }
 
2291
 
 
2292
        /* Create or attach to the shared client hostname buffer */
 
2293
        size = mul_size(NAMEDATALEN, MaxBackends);
 
2294
        BackendClientHostnameBuffer = (char *)
 
2295
                ShmemInitStruct("Backend Client Host Name Buffer", size, &found);
 
2296
 
 
2297
        if (!found)
 
2298
        {
 
2299
                MemSet(BackendClientHostnameBuffer, 0, size);
 
2300
 
 
2301
                /* Initialize st_clienthostname pointers. */
 
2302
                buffer = BackendClientHostnameBuffer;
 
2303
                for (i = 0; i < MaxBackends; i++)
 
2304
                {
 
2305
                        BackendStatusArray[i].st_clienthostname = buffer;
 
2306
                        buffer += NAMEDATALEN;
 
2307
                }
 
2308
        }
 
2309
 
 
2310
        /* Create or attach to the shared activity buffer */
 
2311
        size = mul_size(pgstat_track_activity_query_size, MaxBackends);
 
2312
        BackendActivityBuffer = (char *)
 
2313
                ShmemInitStruct("Backend Activity Buffer", size, &found);
 
2314
 
 
2315
        if (!found)
 
2316
        {
 
2317
                MemSet(BackendActivityBuffer, 0, size);
 
2318
 
 
2319
                /* Initialize st_activity pointers. */
 
2320
                buffer = BackendActivityBuffer;
 
2321
                for (i = 0; i < MaxBackends; i++)
 
2322
                {
 
2323
                        BackendStatusArray[i].st_activity = buffer;
 
2324
                        buffer += pgstat_track_activity_query_size;
 
2325
                }
 
2326
        }
 
2327
}
 
2328
 
 
2329
 
 
2330
/* ----------
 
2331
 * pgstat_initialize() -
 
2332
 *
 
2333
 *      Initialize pgstats state, and set up our on-proc-exit hook.
 
2334
 *      Called from InitPostgres.  MyBackendId must be set,
 
2335
 *      but we must not have started any transaction yet (since the
 
2336
 *      exit hook must run after the last transaction exit).
 
2337
 *      NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful.
 
2338
 * ----------
 
2339
 */
 
2340
void
 
2341
pgstat_initialize(void)
 
2342
{
 
2343
        /* Initialize MyBEEntry */
 
2344
        Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
 
2345
        MyBEEntry = &BackendStatusArray[MyBackendId - 1];
 
2346
 
 
2347
        /* Set up a process-exit hook to clean up */
 
2348
        on_shmem_exit(pgstat_beshutdown_hook, 0);
 
2349
}
 
2350
 
 
2351
/* ----------
 
2352
 * pgstat_bestart() -
 
2353
 *
 
2354
 *      Initialize this backend's entry in the PgBackendStatus array.
 
2355
 *      Called from InitPostgres.
 
2356
 *      MyDatabaseId, session userid, and application_name must be set
 
2357
 *      (hence, this cannot be combined with pgstat_initialize).
 
2358
 * ----------
 
2359
 */
 
2360
void
 
2361
pgstat_bestart(void)
 
2362
{
 
2363
        TimestampTz proc_start_timestamp;
 
2364
        Oid                     userid;
 
2365
        SockAddr        clientaddr;
 
2366
        volatile PgBackendStatus *beentry;
 
2367
 
 
2368
        /*
 
2369
         * To minimize the time spent modifying the PgBackendStatus entry, fetch
 
2370
         * all the needed data first.
 
2371
         *
 
2372
         * If we have a MyProcPort, use its session start time (for consistency,
 
2373
         * and to save a kernel call).
 
2374
         */
 
2375
        if (MyProcPort)
 
2376
                proc_start_timestamp = MyProcPort->SessionStartTime;
 
2377
        else
 
2378
                proc_start_timestamp = GetCurrentTimestamp();
 
2379
        userid = GetSessionUserId();
 
2380
 
 
2381
        /*
 
2382
         * We may not have a MyProcPort (eg, if this is the autovacuum process).
 
2383
         * If so, use all-zeroes client address, which is dealt with specially in
 
2384
         * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port.
 
2385
         */
 
2386
        if (MyProcPort)
 
2387
                memcpy(&clientaddr, &MyProcPort->raddr, sizeof(clientaddr));
 
2388
        else
 
2389
                MemSet(&clientaddr, 0, sizeof(clientaddr));
 
2390
 
 
2391
        /*
 
2392
         * Initialize my status entry, following the protocol of bumping
 
2393
         * st_changecount before and after; and make sure it's even afterwards. We
 
2394
         * use a volatile pointer here to ensure the compiler doesn't try to get
 
2395
         * cute.
 
2396
         */
 
2397
        beentry = MyBEEntry;
 
2398
        do
 
2399
        {
 
2400
                beentry->st_changecount++;
 
2401
        } while ((beentry->st_changecount & 1) == 0);
 
2402
 
 
2403
        beentry->st_procpid = MyProcPid;
 
2404
        beentry->st_proc_start_timestamp = proc_start_timestamp;
 
2405
        beentry->st_activity_start_timestamp = 0;
 
2406
        beentry->st_xact_start_timestamp = 0;
 
2407
        beentry->st_databaseid = MyDatabaseId;
 
2408
        beentry->st_userid = userid;
 
2409
        beentry->st_clientaddr = clientaddr;
 
2410
        beentry->st_clienthostname[0] = '\0';
 
2411
        beentry->st_waiting = false;
 
2412
        beentry->st_appname[0] = '\0';
 
2413
        beentry->st_activity[0] = '\0';
 
2414
        /* Also make sure the last byte in each string area is always 0 */
 
2415
        beentry->st_clienthostname[NAMEDATALEN - 1] = '\0';
 
2416
        beentry->st_appname[NAMEDATALEN - 1] = '\0';
 
2417
        beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0';
 
2418
 
 
2419
        beentry->st_changecount++;
 
2420
        Assert((beentry->st_changecount & 1) == 0);
 
2421
 
 
2422
        if (MyProcPort && MyProcPort->remote_hostname)
 
2423
                strlcpy(beentry->st_clienthostname, MyProcPort->remote_hostname, NAMEDATALEN);
 
2424
 
 
2425
        /* Update app name to current GUC setting */
 
2426
        if (application_name)
 
2427
                pgstat_report_appname(application_name);
 
2428
}
 
2429
 
 
2430
/*
 
2431
 * Shut down a single backend's statistics reporting at process exit.
 
2432
 *
 
2433
 * Flush any remaining statistics counts out to the collector.
 
2434
 * Without this, operations triggered during backend exit (such as
 
2435
 * temp table deletions) won't be counted.
 
2436
 *
 
2437
 * Lastly, clear out our entry in the PgBackendStatus array.
 
2438
 */
 
2439
static void
 
2440
pgstat_beshutdown_hook(int code, Datum arg)
 
2441
{
 
2442
        volatile PgBackendStatus *beentry = MyBEEntry;
 
2443
 
 
2444
        /*
 
2445
         * If we got as far as discovering our own database ID, we can report what
 
2446
         * we did to the collector.  Otherwise, we'd be sending an invalid
 
2447
         * database ID, so forget it.  (This means that accesses to pg_database
 
2448
         * during failed backend starts might never get counted.)
 
2449
         */
 
2450
        if (OidIsValid(MyDatabaseId))
 
2451
                pgstat_report_stat(true);
 
2452
 
 
2453
        /*
 
2454
         * Clear my status entry, following the protocol of bumping st_changecount
 
2455
         * before and after.  We use a volatile pointer here to ensure the
 
2456
         * compiler doesn't try to get cute.
 
2457
         */
 
2458
        beentry->st_changecount++;
 
2459
 
 
2460
        beentry->st_procpid = 0;        /* mark invalid */
 
2461
 
 
2462
        beentry->st_changecount++;
 
2463
        Assert((beentry->st_changecount & 1) == 0);
 
2464
}
 
2465
 
 
2466
 
 
2467
/* ----------
 
2468
 * pgstat_report_activity() -
 
2469
 *
 
2470
 *      Called from tcop/postgres.c to report what the backend is actually doing
 
2471
 *      (usually "<IDLE>" or the start of the query to be executed).
 
2472
 * ----------
 
2473
 */
 
2474
void
 
2475
pgstat_report_activity(const char *cmd_str)
 
2476
{
 
2477
        volatile PgBackendStatus *beentry = MyBEEntry;
 
2478
        TimestampTz start_timestamp;
 
2479
        int                     len;
 
2480
 
 
2481
        TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str);
 
2482
 
 
2483
        if (!pgstat_track_activities || !beentry)
 
2484
                return;
 
2485
 
 
2486
        /*
 
2487
         * To minimize the time spent modifying the entry, fetch all the needed
 
2488
         * data first.
 
2489
         */
 
2490
        start_timestamp = GetCurrentStatementStartTimestamp();
 
2491
 
 
2492
        len = strlen(cmd_str);
 
2493
        len = pg_mbcliplen(cmd_str, len, pgstat_track_activity_query_size - 1);
 
2494
 
 
2495
        /*
 
2496
         * Update my status entry, following the protocol of bumping
 
2497
         * st_changecount before and after.  We use a volatile pointer here to
 
2498
         * ensure the compiler doesn't try to get cute.
 
2499
         */
 
2500
        beentry->st_changecount++;
 
2501
 
 
2502
        beentry->st_activity_start_timestamp = start_timestamp;
 
2503
        memcpy((char *) beentry->st_activity, cmd_str, len);
 
2504
        beentry->st_activity[len] = '\0';
 
2505
 
 
2506
        beentry->st_changecount++;
 
2507
        Assert((beentry->st_changecount & 1) == 0);
 
2508
}
 
2509
 
 
2510
/* ----------
 
2511
 * pgstat_report_appname() -
 
2512
 *
 
2513
 *      Called to update our application name.
 
2514
 * ----------
 
2515
 */
 
2516
void
 
2517
pgstat_report_appname(const char *appname)
 
2518
{
 
2519
        volatile PgBackendStatus *beentry = MyBEEntry;
 
2520
        int                     len;
 
2521
 
 
2522
        if (!beentry)
 
2523
                return;
 
2524
 
 
2525
        /* This should be unnecessary if GUC did its job, but be safe */
 
2526
        len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1);
 
2527
 
 
2528
        /*
 
2529
         * Update my status entry, following the protocol of bumping
 
2530
         * st_changecount before and after.  We use a volatile pointer here to
 
2531
         * ensure the compiler doesn't try to get cute.
 
2532
         */
 
2533
        beentry->st_changecount++;
 
2534
 
 
2535
        memcpy((char *) beentry->st_appname, appname, len);
 
2536
        beentry->st_appname[len] = '\0';
 
2537
 
 
2538
        beentry->st_changecount++;
 
2539
        Assert((beentry->st_changecount & 1) == 0);
 
2540
}
 
2541
 
 
2542
/*
 
2543
 * Report current transaction start timestamp as the specified value.
 
2544
 * Zero means there is no active transaction.
 
2545
 */
 
2546
void
 
2547
pgstat_report_xact_timestamp(TimestampTz tstamp)
 
2548
{
 
2549
        volatile PgBackendStatus *beentry = MyBEEntry;
 
2550
 
 
2551
        if (!pgstat_track_activities || !beentry)
 
2552
                return;
 
2553
 
 
2554
        /*
 
2555
         * Update my status entry, following the protocol of bumping
 
2556
         * st_changecount before and after.  We use a volatile pointer here to
 
2557
         * ensure the compiler doesn't try to get cute.
 
2558
         */
 
2559
        beentry->st_changecount++;
 
2560
        beentry->st_xact_start_timestamp = tstamp;
 
2561
        beentry->st_changecount++;
 
2562
        Assert((beentry->st_changecount & 1) == 0);
 
2563
}
 
2564
 
 
2565
/* ----------
 
2566
 * pgstat_report_waiting() -
 
2567
 *
 
2568
 *      Called from lock manager to report beginning or end of a lock wait.
 
2569
 *
 
2570
 * NB: this *must* be able to survive being called before MyBEEntry has been
 
2571
 * initialized.
 
2572
 * ----------
 
2573
 */
 
2574
void
 
2575
pgstat_report_waiting(bool waiting)
 
2576
{
 
2577
        volatile PgBackendStatus *beentry = MyBEEntry;
 
2578
 
 
2579
        if (!pgstat_track_activities || !beentry)
 
2580
                return;
 
2581
 
 
2582
        /*
 
2583
         * Since this is a single-byte field in a struct that only this process
 
2584
         * may modify, there seems no need to bother with the st_changecount
 
2585
         * protocol.  The update must appear atomic in any case.
 
2586
         */
 
2587
        beentry->st_waiting = waiting;
 
2588
}
 
2589
 
 
2590
 
 
2591
/* ----------
 
2592
 * pgstat_read_current_status() -
 
2593
 *
 
2594
 *      Copy the current contents of the PgBackendStatus array to local memory,
 
2595
 *      if not already done in this transaction.
 
2596
 * ----------
 
2597
 */
 
2598
static void
 
2599
pgstat_read_current_status(void)
 
2600
{
 
2601
        volatile PgBackendStatus *beentry;
 
2602
        PgBackendStatus *localtable;
 
2603
        PgBackendStatus *localentry;
 
2604
        char       *localappname,
 
2605
                           *localactivity;
 
2606
        int                     i;
 
2607
 
 
2608
        Assert(!pgStatRunningInCollector);
 
2609
        if (localBackendStatusTable)
 
2610
                return;                                 /* already done */
 
2611
 
 
2612
        pgstat_setup_memcxt();
 
2613
 
 
2614
        localtable = (PgBackendStatus *)
 
2615
                MemoryContextAlloc(pgStatLocalContext,
 
2616
                                                   sizeof(PgBackendStatus) * MaxBackends);
 
2617
        localappname = (char *)
 
2618
                MemoryContextAlloc(pgStatLocalContext,
 
2619
                                                   NAMEDATALEN * MaxBackends);
 
2620
        localactivity = (char *)
 
2621
                MemoryContextAlloc(pgStatLocalContext,
 
2622
                                                   pgstat_track_activity_query_size * MaxBackends);
 
2623
        localNumBackends = 0;
 
2624
 
 
2625
        beentry = BackendStatusArray;
 
2626
        localentry = localtable;
 
2627
        for (i = 1; i <= MaxBackends; i++)
 
2628
        {
 
2629
                /*
 
2630
                 * Follow the protocol of retrying if st_changecount changes while we
 
2631
                 * copy the entry, or if it's odd.  (The check for odd is needed to
 
2632
                 * cover the case where we are able to completely copy the entry while
 
2633
                 * the source backend is between increment steps.)      We use a volatile
 
2634
                 * pointer here to ensure the compiler doesn't try to get cute.
 
2635
                 */
 
2636
                for (;;)
 
2637
                {
 
2638
                        int                     save_changecount = beentry->st_changecount;
 
2639
 
 
2640
                        localentry->st_procpid = beentry->st_procpid;
 
2641
                        if (localentry->st_procpid > 0)
 
2642
                        {
 
2643
                                memcpy(localentry, (char *) beentry, sizeof(PgBackendStatus));
 
2644
 
 
2645
                                /*
 
2646
                                 * strcpy is safe even if the string is modified concurrently,
 
2647
                                 * because there's always a \0 at the end of the buffer.
 
2648
                                 */
 
2649
                                strcpy(localappname, (char *) beentry->st_appname);
 
2650
                                localentry->st_appname = localappname;
 
2651
                                strcpy(localactivity, (char *) beentry->st_activity);
 
2652
                                localentry->st_activity = localactivity;
 
2653
                        }
 
2654
 
 
2655
                        if (save_changecount == beentry->st_changecount &&
 
2656
                                (save_changecount & 1) == 0)
 
2657
                                break;
 
2658
 
 
2659
                        /* Make sure we can break out of loop if stuck... */
 
2660
                        CHECK_FOR_INTERRUPTS();
 
2661
                }
 
2662
 
 
2663
                beentry++;
 
2664
                /* Only valid entries get included into the local array */
 
2665
                if (localentry->st_procpid > 0)
 
2666
                {
 
2667
                        localentry++;
 
2668
                        localappname += NAMEDATALEN;
 
2669
                        localactivity += pgstat_track_activity_query_size;
 
2670
                        localNumBackends++;
 
2671
                }
 
2672
        }
 
2673
 
 
2674
        /* Set the pointer only after completion of a valid table */
 
2675
        localBackendStatusTable = localtable;
 
2676
}
 
2677
 
 
2678
 
 
2679
/* ----------
 
2680
 * pgstat_get_backend_current_activity() -
 
2681
 *
 
2682
 *      Return a string representing the current activity of the backend with
 
2683
 *      the specified PID.      This looks directly at the BackendStatusArray,
 
2684
 *      and so will provide current information regardless of the age of our
 
2685
 *      transaction's snapshot of the status array.
 
2686
 *
 
2687
 *      It is the caller's responsibility to invoke this only for backends whose
 
2688
 *      state is expected to remain stable while the result is in use.  The
 
2689
 *      only current use is in deadlock reporting, where we can expect that
 
2690
 *      the target backend is blocked on a lock.  (There are corner cases
 
2691
 *      where the target's wait could get aborted while we are looking at it,
 
2692
 *      but the very worst consequence is to return a pointer to a string
 
2693
 *      that's been changed, so we won't worry too much.)
 
2694
 *
 
2695
 *      Note: return strings for special cases match pg_stat_get_backend_activity.
 
2696
 * ----------
 
2697
 */
 
2698
const char *
 
2699
pgstat_get_backend_current_activity(int pid, bool checkUser)
 
2700
{
 
2701
        PgBackendStatus *beentry;
 
2702
        int                     i;
 
2703
 
 
2704
        beentry = BackendStatusArray;
 
2705
        for (i = 1; i <= MaxBackends; i++)
 
2706
        {
 
2707
                /*
 
2708
                 * Although we expect the target backend's entry to be stable, that
 
2709
                 * doesn't imply that anyone else's is.  To avoid identifying the
 
2710
                 * wrong backend, while we check for a match to the desired PID we
 
2711
                 * must follow the protocol of retrying if st_changecount changes
 
2712
                 * while we examine the entry, or if it's odd.  (This might be
 
2713
                 * unnecessary, since fetching or storing an int is almost certainly
 
2714
                 * atomic, but let's play it safe.)  We use a volatile pointer here to
 
2715
                 * ensure the compiler doesn't try to get cute.
 
2716
                 */
 
2717
                volatile PgBackendStatus *vbeentry = beentry;
 
2718
                bool            found;
 
2719
 
 
2720
                for (;;)
 
2721
                {
 
2722
                        int                     save_changecount = vbeentry->st_changecount;
 
2723
 
 
2724
                        found = (vbeentry->st_procpid == pid);
 
2725
 
 
2726
                        if (save_changecount == vbeentry->st_changecount &&
 
2727
                                (save_changecount & 1) == 0)
 
2728
                                break;
 
2729
 
 
2730
                        /* Make sure we can break out of loop if stuck... */
 
2731
                        CHECK_FOR_INTERRUPTS();
 
2732
                }
 
2733
 
 
2734
                if (found)
 
2735
                {
 
2736
                        /* Now it is safe to use the non-volatile pointer */
 
2737
                        if (checkUser && !superuser() && beentry->st_userid != GetUserId())
 
2738
                                return "<insufficient privilege>";
 
2739
                        else if (*(beentry->st_activity) == '\0')
 
2740
                                return "<command string not enabled>";
 
2741
                        else
 
2742
                                return beentry->st_activity;
 
2743
                }
 
2744
 
 
2745
                beentry++;
 
2746
        }
 
2747
 
 
2748
        /* If we get here, caller is in error ... */
 
2749
        return "<backend information not available>";
 
2750
}
 
2751
 
 
2752
 
 
2753
/* ------------------------------------------------------------
 
2754
 * Local support functions follow
 
2755
 * ------------------------------------------------------------
 
2756
 */
 
2757
 
 
2758
 
 
2759
/* ----------
 
2760
 * pgstat_setheader() -
 
2761
 *
 
2762
 *              Set common header fields in a statistics message
 
2763
 * ----------
 
2764
 */
 
2765
static void
 
2766
pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype)
 
2767
{
 
2768
        hdr->m_type = mtype;
 
2769
}
 
2770
 
 
2771
 
 
2772
/* ----------
 
2773
 * pgstat_send() -
 
2774
 *
 
2775
 *              Send out one statistics message to the collector
 
2776
 * ----------
 
2777
 */
 
2778
static void
 
2779
pgstat_send(void *msg, int len)
 
2780
{
 
2781
        int                     rc;
 
2782
 
 
2783
        if (pgStatSock == PGINVALID_SOCKET)
 
2784
                return;
 
2785
 
 
2786
        ((PgStat_MsgHdr *) msg)->m_size = len;
 
2787
 
 
2788
        /* We'll retry after EINTR, but ignore all other failures */
 
2789
        do
 
2790
        {
 
2791
                rc = send(pgStatSock, msg, len, 0);
 
2792
        } while (rc < 0 && errno == EINTR);
 
2793
 
 
2794
#ifdef USE_ASSERT_CHECKING
 
2795
        /* In debug builds, log send failures ... */
 
2796
        if (rc < 0)
 
2797
                elog(LOG, "could not send to statistics collector: %m");
 
2798
#endif
 
2799
}
 
2800
 
 
2801
/* ----------
 
2802
 * pgstat_send_bgwriter() -
 
2803
 *
 
2804
 *              Send bgwriter statistics to the collector
 
2805
 * ----------
 
2806
 */
 
2807
void
 
2808
pgstat_send_bgwriter(void)
 
2809
{
 
2810
        /* We assume this initializes to zeroes */
 
2811
        static const PgStat_MsgBgWriter all_zeroes;
 
2812
 
 
2813
        /*
 
2814
         * This function can be called even if nothing at all has happened. In
 
2815
         * this case, avoid sending a completely empty message to the stats
 
2816
         * collector.
 
2817
         */
 
2818
        if (memcmp(&BgWriterStats, &all_zeroes, sizeof(PgStat_MsgBgWriter)) == 0)
 
2819
                return;
 
2820
 
 
2821
        /*
 
2822
         * Prepare and send the message
 
2823
         */
 
2824
        pgstat_setheader(&BgWriterStats.m_hdr, PGSTAT_MTYPE_BGWRITER);
 
2825
        pgstat_send(&BgWriterStats, sizeof(BgWriterStats));
 
2826
 
 
2827
        /*
 
2828
         * Clear out the statistics buffer, so it can be re-used.
 
2829
         */
 
2830
        MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 
2831
}
 
2832
 
 
2833
 
 
2834
/* ----------
 
2835
 * PgstatCollectorMain() -
 
2836
 *
 
2837
 *      Start up the statistics collector process.      This is the body of the
 
2838
 *      postmaster child process.
 
2839
 *
 
2840
 *      The argc/argv parameters are valid only in EXEC_BACKEND case.
 
2841
 * ----------
 
2842
 */
 
2843
NON_EXEC_STATIC void
 
2844
PgstatCollectorMain(int argc, char *argv[])
 
2845
{
 
2846
        int                     len;
 
2847
        PgStat_Msg      msg;
 
2848
 
 
2849
#ifndef WIN32
 
2850
#ifdef HAVE_POLL
 
2851
        struct pollfd input_fd;
 
2852
#else
 
2853
        struct timeval sel_timeout;
 
2854
        fd_set          rfds;
 
2855
#endif
 
2856
#endif
 
2857
 
 
2858
        IsUnderPostmaster = true;       /* we are a postmaster subprocess now */
 
2859
 
 
2860
        MyProcPid = getpid();           /* reset MyProcPid */
 
2861
 
 
2862
        MyStartTime = time(NULL);       /* record Start Time for logging */
 
2863
 
 
2864
        /*
 
2865
         * If possible, make this process a group leader, so that the postmaster
 
2866
         * can signal any child processes too.  (pgstat probably never has any
 
2867
         * child processes, but for consistency we make all postmaster child
 
2868
         * processes do this.)
 
2869
         */
 
2870
#ifdef HAVE_SETSID
 
2871
        if (setsid() < 0)
 
2872
                elog(FATAL, "setsid() failed: %m");
 
2873
#endif
 
2874
 
 
2875
        /*
 
2876
         * Ignore all signals usually bound to some action in the postmaster,
 
2877
         * except SIGQUIT.
 
2878
         */
 
2879
        pqsignal(SIGHUP, pgstat_sighup_handler);
 
2880
        pqsignal(SIGINT, SIG_IGN);
 
2881
        pqsignal(SIGTERM, SIG_IGN);
 
2882
        pqsignal(SIGQUIT, pgstat_exit);
 
2883
        pqsignal(SIGALRM, SIG_IGN);
 
2884
        pqsignal(SIGPIPE, SIG_IGN);
 
2885
        pqsignal(SIGUSR1, SIG_IGN);
 
2886
        pqsignal(SIGUSR2, SIG_IGN);
 
2887
        pqsignal(SIGCHLD, SIG_DFL);
 
2888
        pqsignal(SIGTTIN, SIG_DFL);
 
2889
        pqsignal(SIGTTOU, SIG_DFL);
 
2890
        pqsignal(SIGCONT, SIG_DFL);
 
2891
        pqsignal(SIGWINCH, SIG_DFL);
 
2892
        PG_SETMASK(&UnBlockSig);
 
2893
 
 
2894
        /*
 
2895
         * Identify myself via ps
 
2896
         */
 
2897
        init_ps_display("stats collector process", "", "", "");
 
2898
 
 
2899
        /*
 
2900
         * Arrange to write the initial status file right away
 
2901
         */
 
2902
        last_statrequest = GetCurrentTimestamp();
 
2903
        last_statwrite = last_statrequest - 1;
 
2904
 
 
2905
        /*
 
2906
         * Read in an existing statistics stats file or initialize the stats to
 
2907
         * zero.
 
2908
         */
 
2909
        pgStatRunningInCollector = true;
 
2910
        pgStatDBHash = pgstat_read_statsfile(InvalidOid, true);
 
2911
 
 
2912
        /*
 
2913
         * Setup the descriptor set for select(2).      Since only one bit in the set
 
2914
         * ever changes, we need not repeat FD_ZERO each time.
 
2915
         */
 
2916
#if !defined(HAVE_POLL) && !defined(WIN32)
 
2917
        FD_ZERO(&rfds);
 
2918
#endif
 
2919
 
 
2920
        /*
 
2921
         * Loop to process messages until we get SIGQUIT or detect ungraceful
 
2922
         * death of our parent postmaster.
 
2923
         *
 
2924
         * For performance reasons, we don't want to do a PostmasterIsAlive() test
 
2925
         * after every message; instead, do it only when select()/poll() is
 
2926
         * interrupted by timeout.      In essence, we'll stay alive as long as
 
2927
         * backends keep sending us stuff often, even if the postmaster is gone.
 
2928
         */
 
2929
        for (;;)
 
2930
        {
 
2931
                int                     got_data;
 
2932
 
 
2933
                /*
 
2934
                 * Quit if we get SIGQUIT from the postmaster.
 
2935
                 */
 
2936
                if (need_exit)
 
2937
                        break;
 
2938
 
 
2939
                /*
 
2940
                 * Reload configuration if we got SIGHUP from the postmaster.
 
2941
                 */
 
2942
                if (got_SIGHUP)
 
2943
                {
 
2944
                        ProcessConfigFile(PGC_SIGHUP);
 
2945
                        got_SIGHUP = false;
 
2946
                }
 
2947
 
 
2948
                /*
 
2949
                 * Write the stats file if a new request has arrived that is not
 
2950
                 * satisfied by existing file.
 
2951
                 */
 
2952
                if (last_statwrite < last_statrequest)
 
2953
                        pgstat_write_statsfile(false);
 
2954
 
 
2955
                /*
 
2956
                 * Wait for a message to arrive; but not for more than
 
2957
                 * PGSTAT_SELECT_TIMEOUT seconds. (This determines how quickly we will
 
2958
                 * shut down after an ungraceful postmaster termination; so it needn't
 
2959
                 * be very fast.  However, on some systems SIGQUIT won't interrupt the
 
2960
                 * poll/select call, so this also limits speed of response to SIGQUIT,
 
2961
                 * which is more important.)
 
2962
                 *
 
2963
                 * We use poll(2) if available, otherwise select(2). Win32 has its own
 
2964
                 * implementation.
 
2965
                 */
 
2966
#ifndef WIN32
 
2967
#ifdef HAVE_POLL
 
2968
                input_fd.fd = pgStatSock;
 
2969
                input_fd.events = POLLIN | POLLERR;
 
2970
                input_fd.revents = 0;
 
2971
 
 
2972
                if (poll(&input_fd, 1, PGSTAT_SELECT_TIMEOUT * 1000) < 0)
 
2973
                {
 
2974
                        if (errno == EINTR)
 
2975
                                continue;
 
2976
                        ereport(ERROR,
 
2977
                                        (errcode_for_socket_access(),
 
2978
                                         errmsg("poll() failed in statistics collector: %m")));
 
2979
                }
 
2980
 
 
2981
                got_data = (input_fd.revents != 0);
 
2982
#else                                                   /* !HAVE_POLL */
 
2983
 
 
2984
                FD_SET(pgStatSock, &rfds);
 
2985
 
 
2986
                /*
 
2987
                 * timeout struct is modified by select() on some operating systems,
 
2988
                 * so re-fill it each time.
 
2989
                 */
 
2990
                sel_timeout.tv_sec = PGSTAT_SELECT_TIMEOUT;
 
2991
                sel_timeout.tv_usec = 0;
 
2992
 
 
2993
                if (select(pgStatSock + 1, &rfds, NULL, NULL, &sel_timeout) < 0)
 
2994
                {
 
2995
                        if (errno == EINTR)
 
2996
                                continue;
 
2997
                        ereport(ERROR,
 
2998
                                        (errcode_for_socket_access(),
 
2999
                                         errmsg("select() failed in statistics collector: %m")));
 
3000
                }
 
3001
 
 
3002
                got_data = FD_ISSET(pgStatSock, &rfds);
 
3003
#endif   /* HAVE_POLL */
 
3004
#else                                                   /* WIN32 */
 
3005
                got_data = pgwin32_waitforsinglesocket(pgStatSock, FD_READ,
 
3006
                                                                                           PGSTAT_SELECT_TIMEOUT * 1000);
 
3007
#endif
 
3008
 
 
3009
                /*
 
3010
                 * If there is a message on the socket, read it and check for
 
3011
                 * validity.
 
3012
                 */
 
3013
                if (got_data)
 
3014
                {
 
3015
                        len = recv(pgStatSock, (char *) &msg,
 
3016
                                           sizeof(PgStat_Msg), 0);
 
3017
                        if (len < 0)
 
3018
                        {
 
3019
                                if (errno == EINTR)
 
3020
                                        continue;
 
3021
                                ereport(ERROR,
 
3022
                                                (errcode_for_socket_access(),
 
3023
                                                 errmsg("could not read statistics message: %m")));
 
3024
                        }
 
3025
 
 
3026
                        /*
 
3027
                         * We ignore messages that are smaller than our common header
 
3028
                         */
 
3029
                        if (len < sizeof(PgStat_MsgHdr))
 
3030
                                continue;
 
3031
 
 
3032
                        /*
 
3033
                         * The received length must match the length in the header
 
3034
                         */
 
3035
                        if (msg.msg_hdr.m_size != len)
 
3036
                                continue;
 
3037
 
 
3038
                        /*
 
3039
                         * O.K. - we accept this message.  Process it.
 
3040
                         */
 
3041
                        switch (msg.msg_hdr.m_type)
 
3042
                        {
 
3043
                                case PGSTAT_MTYPE_DUMMY:
 
3044
                                        break;
 
3045
 
 
3046
                                case PGSTAT_MTYPE_INQUIRY:
 
3047
                                        pgstat_recv_inquiry((PgStat_MsgInquiry *) &msg, len);
 
3048
                                        break;
 
3049
 
 
3050
                                case PGSTAT_MTYPE_TABSTAT:
 
3051
                                        pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, len);
 
3052
                                        break;
 
3053
 
 
3054
                                case PGSTAT_MTYPE_TABPURGE:
 
3055
                                        pgstat_recv_tabpurge((PgStat_MsgTabpurge *) &msg, len);
 
3056
                                        break;
 
3057
 
 
3058
                                case PGSTAT_MTYPE_DROPDB:
 
3059
                                        pgstat_recv_dropdb((PgStat_MsgDropdb *) &msg, len);
 
3060
                                        break;
 
3061
 
 
3062
                                case PGSTAT_MTYPE_RESETCOUNTER:
 
3063
                                        pgstat_recv_resetcounter((PgStat_MsgResetcounter *) &msg,
 
3064
                                                                                         len);
 
3065
                                        break;
 
3066
 
 
3067
                                case PGSTAT_MTYPE_RESETSHAREDCOUNTER:
 
3068
                                        pgstat_recv_resetsharedcounter(
 
3069
                                                                           (PgStat_MsgResetsharedcounter *) &msg,
 
3070
                                                                                                   len);
 
3071
                                        break;
 
3072
 
 
3073
                                case PGSTAT_MTYPE_RESETSINGLECOUNTER:
 
3074
                                        pgstat_recv_resetsinglecounter(
 
3075
                                                                           (PgStat_MsgResetsinglecounter *) &msg,
 
3076
                                                                                                   len);
 
3077
                                        break;
 
3078
 
 
3079
                                case PGSTAT_MTYPE_AUTOVAC_START:
 
3080
                                        pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, len);
 
3081
                                        break;
 
3082
 
 
3083
                                case PGSTAT_MTYPE_VACUUM:
 
3084
                                        pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, len);
 
3085
                                        break;
 
3086
 
 
3087
                                case PGSTAT_MTYPE_ANALYZE:
 
3088
                                        pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, len);
 
3089
                                        break;
 
3090
 
 
3091
                                case PGSTAT_MTYPE_BGWRITER:
 
3092
                                        pgstat_recv_bgwriter((PgStat_MsgBgWriter *) &msg, len);
 
3093
                                        break;
 
3094
 
 
3095
                                case PGSTAT_MTYPE_FUNCSTAT:
 
3096
                                        pgstat_recv_funcstat((PgStat_MsgFuncstat *) &msg, len);
 
3097
                                        break;
 
3098
 
 
3099
                                case PGSTAT_MTYPE_FUNCPURGE:
 
3100
                                        pgstat_recv_funcpurge((PgStat_MsgFuncpurge *) &msg, len);
 
3101
                                        break;
 
3102
 
 
3103
                                case PGSTAT_MTYPE_RECOVERYCONFLICT:
 
3104
                                        pgstat_recv_recoveryconflict((PgStat_MsgRecoveryConflict *) &msg, len);
 
3105
                                        break;
 
3106
 
 
3107
                                default:
 
3108
                                        break;
 
3109
                        }
 
3110
                }
 
3111
                else
 
3112
                {
 
3113
                        /*
 
3114
                         * We can only get here if the select/poll timeout elapsed. Check
 
3115
                         * for postmaster death.
 
3116
                         */
 
3117
                        if (!PostmasterIsAlive(true))
 
3118
                                break;
 
3119
                }
 
3120
        }                                                       /* end of message-processing loop */
 
3121
 
 
3122
        /*
 
3123
         * Save the final stats to reuse at next startup.
 
3124
         */
 
3125
        pgstat_write_statsfile(true);
 
3126
 
 
3127
        exit(0);
 
3128
}
 
3129
 
 
3130
 
 
3131
/* SIGQUIT signal handler for collector process */
 
3132
static void
 
3133
pgstat_exit(SIGNAL_ARGS)
 
3134
{
 
3135
        need_exit = true;
 
3136
}
 
3137
 
 
3138
/* SIGHUP handler for collector process */
 
3139
static void
 
3140
pgstat_sighup_handler(SIGNAL_ARGS)
 
3141
{
 
3142
        got_SIGHUP = true;
 
3143
}
 
3144
 
 
3145
 
 
3146
/*
 
3147
 * Lookup the hash table entry for the specified database. If no hash
 
3148
 * table entry exists, initialize it, if the create parameter is true.
 
3149
 * Else, return NULL.
 
3150
 */
 
3151
static PgStat_StatDBEntry *
 
3152
pgstat_get_db_entry(Oid databaseid, bool create)
 
3153
{
 
3154
        PgStat_StatDBEntry *result;
 
3155
        bool            found;
 
3156
        HASHACTION      action = (create ? HASH_ENTER : HASH_FIND);
 
3157
 
 
3158
        /* Lookup or create the hash table entry for this database */
 
3159
        result = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
 
3160
                                                                                                &databaseid,
 
3161
                                                                                                action, &found);
 
3162
 
 
3163
        if (!create && !found)
 
3164
                return NULL;
 
3165
 
 
3166
        /* If not found, initialize the new one. */
 
3167
        if (!found)
 
3168
        {
 
3169
                HASHCTL         hash_ctl;
 
3170
 
 
3171
                result->tables = NULL;
 
3172
                result->functions = NULL;
 
3173
                result->n_xact_commit = 0;
 
3174
                result->n_xact_rollback = 0;
 
3175
                result->n_blocks_fetched = 0;
 
3176
                result->n_blocks_hit = 0;
 
3177
                result->n_tuples_returned = 0;
 
3178
                result->n_tuples_fetched = 0;
 
3179
                result->n_tuples_inserted = 0;
 
3180
                result->n_tuples_updated = 0;
 
3181
                result->n_tuples_deleted = 0;
 
3182
                result->last_autovac_time = 0;
 
3183
                result->n_conflict_tablespace = 0;
 
3184
                result->n_conflict_lock = 0;
 
3185
                result->n_conflict_snapshot = 0;
 
3186
                result->n_conflict_bufferpin = 0;
 
3187
                result->n_conflict_startup_deadlock = 0;
 
3188
 
 
3189
                result->stat_reset_timestamp = GetCurrentTimestamp();
 
3190
 
 
3191
                memset(&hash_ctl, 0, sizeof(hash_ctl));
 
3192
                hash_ctl.keysize = sizeof(Oid);
 
3193
                hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
 
3194
                hash_ctl.hash = oid_hash;
 
3195
                result->tables = hash_create("Per-database table",
 
3196
                                                                         PGSTAT_TAB_HASH_SIZE,
 
3197
                                                                         &hash_ctl,
 
3198
                                                                         HASH_ELEM | HASH_FUNCTION);
 
3199
 
 
3200
                hash_ctl.keysize = sizeof(Oid);
 
3201
                hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
 
3202
                hash_ctl.hash = oid_hash;
 
3203
                result->functions = hash_create("Per-database function",
 
3204
                                                                                PGSTAT_FUNCTION_HASH_SIZE,
 
3205
                                                                                &hash_ctl,
 
3206
                                                                                HASH_ELEM | HASH_FUNCTION);
 
3207
        }
 
3208
 
 
3209
        return result;
 
3210
}
 
3211
 
 
3212
 
 
3213
/*
 
3214
 * Lookup the hash table entry for the specified table. If no hash
 
3215
 * table entry exists, initialize it, if the create parameter is true.
 
3216
 * Else, return NULL.
 
3217
 */
 
3218
static PgStat_StatTabEntry *
 
3219
pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create)
 
3220
{
 
3221
        PgStat_StatTabEntry *result;
 
3222
        bool            found;
 
3223
        HASHACTION      action = (create ? HASH_ENTER : HASH_FIND);
 
3224
 
 
3225
        /* Lookup or create the hash table entry for this table */
 
3226
        result = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
 
3227
                                                                                                 &tableoid,
 
3228
                                                                                                 action, &found);
 
3229
 
 
3230
        if (!create && !found)
 
3231
                return NULL;
 
3232
 
 
3233
        /* If not found, initialize the new one. */
 
3234
        if (!found)
 
3235
        {
 
3236
                result->numscans = 0;
 
3237
                result->tuples_returned = 0;
 
3238
                result->tuples_fetched = 0;
 
3239
                result->tuples_inserted = 0;
 
3240
                result->tuples_updated = 0;
 
3241
                result->tuples_deleted = 0;
 
3242
                result->tuples_hot_updated = 0;
 
3243
                result->n_live_tuples = 0;
 
3244
                result->n_dead_tuples = 0;
 
3245
                result->changes_since_analyze = 0;
 
3246
                result->blocks_fetched = 0;
 
3247
                result->blocks_hit = 0;
 
3248
                result->vacuum_timestamp = 0;
 
3249
                result->vacuum_count = 0;
 
3250
                result->autovac_vacuum_timestamp = 0;
 
3251
                result->autovac_vacuum_count = 0;
 
3252
                result->analyze_timestamp = 0;
 
3253
                result->analyze_count = 0;
 
3254
                result->autovac_analyze_timestamp = 0;
 
3255
                result->autovac_analyze_count = 0;
 
3256
        }
 
3257
 
 
3258
        return result;
 
3259
}
 
3260
 
 
3261
 
 
3262
/* ----------
 
3263
 * pgstat_write_statsfile() -
 
3264
 *
 
3265
 *      Tell the news.
 
3266
 *      If writing to the permanent file (happens when the collector is
 
3267
 *      shutting down only), remove the temporary file so that backends
 
3268
 *      starting up under a new postmaster can't read the old data before
 
3269
 *      the new collector is ready.
 
3270
 * ----------
 
3271
 */
 
3272
static void
 
3273
pgstat_write_statsfile(bool permanent)
 
3274
{
 
3275
        HASH_SEQ_STATUS hstat;
 
3276
        HASH_SEQ_STATUS tstat;
 
3277
        HASH_SEQ_STATUS fstat;
 
3278
        PgStat_StatDBEntry *dbentry;
 
3279
        PgStat_StatTabEntry *tabentry;
 
3280
        PgStat_StatFuncEntry *funcentry;
 
3281
        FILE       *fpout;
 
3282
        int32           format_id;
 
3283
        const char *tmpfile = permanent ? PGSTAT_STAT_PERMANENT_TMPFILE : pgstat_stat_tmpname;
 
3284
        const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
 
3285
 
 
3286
        /*
 
3287
         * Open the statistics temp file to write out the current values.
 
3288
         */
 
3289
        fpout = AllocateFile(tmpfile, PG_BINARY_W);
 
3290
        if (fpout == NULL)
 
3291
        {
 
3292
                ereport(LOG,
 
3293
                                (errcode_for_file_access(),
 
3294
                                 errmsg("could not open temporary statistics file \"%s\": %m",
 
3295
                                                tmpfile)));
 
3296
                return;
 
3297
        }
 
3298
 
 
3299
        /*
 
3300
         * Set the timestamp of the stats file.
 
3301
         */
 
3302
        globalStats.stats_timestamp = GetCurrentTimestamp();
 
3303
 
 
3304
        /*
 
3305
         * Write the file header --- currently just a format ID.
 
3306
         */
 
3307
        format_id = PGSTAT_FILE_FORMAT_ID;
 
3308
        fwrite(&format_id, sizeof(format_id), 1, fpout);
 
3309
 
 
3310
        /*
 
3311
         * Write global stats struct
 
3312
         */
 
3313
        fwrite(&globalStats, sizeof(globalStats), 1, fpout);
 
3314
 
 
3315
        /*
 
3316
         * Walk through the database table.
 
3317
         */
 
3318
        hash_seq_init(&hstat, pgStatDBHash);
 
3319
        while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL)
 
3320
        {
 
3321
                /*
 
3322
                 * Write out the DB entry including the number of live backends. We
 
3323
                 * don't write the tables or functions pointers, since they're of no
 
3324
                 * use to any other process.
 
3325
                 */
 
3326
                fputc('D', fpout);
 
3327
                fwrite(dbentry, offsetof(PgStat_StatDBEntry, tables), 1, fpout);
 
3328
 
 
3329
                /*
 
3330
                 * Walk through the database's access stats per table.
 
3331
                 */
 
3332
                hash_seq_init(&tstat, dbentry->tables);
 
3333
                while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&tstat)) != NULL)
 
3334
                {
 
3335
                        fputc('T', fpout);
 
3336
                        fwrite(tabentry, sizeof(PgStat_StatTabEntry), 1, fpout);
 
3337
                }
 
3338
 
 
3339
                /*
 
3340
                 * Walk through the database's function stats table.
 
3341
                 */
 
3342
                hash_seq_init(&fstat, dbentry->functions);
 
3343
                while ((funcentry = (PgStat_StatFuncEntry *) hash_seq_search(&fstat)) != NULL)
 
3344
                {
 
3345
                        fputc('F', fpout);
 
3346
                        fwrite(funcentry, sizeof(PgStat_StatFuncEntry), 1, fpout);
 
3347
                }
 
3348
 
 
3349
                /*
 
3350
                 * Mark the end of this DB
 
3351
                 */
 
3352
                fputc('d', fpout);
 
3353
        }
 
3354
 
 
3355
        /*
 
3356
         * No more output to be done. Close the temp file and replace the old
 
3357
         * pgstat.stat with it.  The ferror() check replaces testing for error
 
3358
         * after each individual fputc or fwrite above.
 
3359
         */
 
3360
        fputc('E', fpout);
 
3361
 
 
3362
        if (ferror(fpout))
 
3363
        {
 
3364
                ereport(LOG,
 
3365
                                (errcode_for_file_access(),
 
3366
                           errmsg("could not write temporary statistics file \"%s\": %m",
 
3367
                                          tmpfile)));
 
3368
                FreeFile(fpout);
 
3369
                unlink(tmpfile);
 
3370
        }
 
3371
        else if (FreeFile(fpout) < 0)
 
3372
        {
 
3373
                ereport(LOG,
 
3374
                                (errcode_for_file_access(),
 
3375
                           errmsg("could not close temporary statistics file \"%s\": %m",
 
3376
                                          tmpfile)));
 
3377
                unlink(tmpfile);
 
3378
        }
 
3379
        else if (rename(tmpfile, statfile) < 0)
 
3380
        {
 
3381
                ereport(LOG,
 
3382
                                (errcode_for_file_access(),
 
3383
                                 errmsg("could not rename temporary statistics file \"%s\" to \"%s\": %m",
 
3384
                                                tmpfile, statfile)));
 
3385
                unlink(tmpfile);
 
3386
        }
 
3387
        else
 
3388
        {
 
3389
                /*
 
3390
                 * Successful write, so update last_statwrite.
 
3391
                 */
 
3392
                last_statwrite = globalStats.stats_timestamp;
 
3393
 
 
3394
                /*
 
3395
                 * If there is clock skew between backends and the collector, we could
 
3396
                 * receive a stats request time that's in the future.  If so, complain
 
3397
                 * and reset last_statrequest.  Resetting ensures that no inquiry
 
3398
                 * message can cause more than one stats file write to occur.
 
3399
                 */
 
3400
                if (last_statrequest > last_statwrite)
 
3401
                {
 
3402
                        char       *reqtime;
 
3403
                        char       *mytime;
 
3404
 
 
3405
                        /* Copy because timestamptz_to_str returns a static buffer */
 
3406
                        reqtime = pstrdup(timestamptz_to_str(last_statrequest));
 
3407
                        mytime = pstrdup(timestamptz_to_str(last_statwrite));
 
3408
                        elog(LOG, "last_statrequest %s is later than collector's time %s",
 
3409
                                 reqtime, mytime);
 
3410
                        pfree(reqtime);
 
3411
                        pfree(mytime);
 
3412
 
 
3413
                        last_statrequest = last_statwrite;
 
3414
                }
 
3415
        }
 
3416
 
 
3417
        if (permanent)
 
3418
                unlink(pgstat_stat_filename);
 
3419
}
 
3420
 
 
3421
 
 
3422
/* ----------
 
3423
 * pgstat_read_statsfile() -
 
3424
 *
 
3425
 *      Reads in an existing statistics collector file and initializes the
 
3426
 *      databases' hash table (whose entries point to the tables' hash tables).
 
3427
 * ----------
 
3428
 */
 
3429
static HTAB *
 
3430
pgstat_read_statsfile(Oid onlydb, bool permanent)
 
3431
{
 
3432
        PgStat_StatDBEntry *dbentry;
 
3433
        PgStat_StatDBEntry dbbuf;
 
3434
        PgStat_StatTabEntry *tabentry;
 
3435
        PgStat_StatTabEntry tabbuf;
 
3436
        PgStat_StatFuncEntry funcbuf;
 
3437
        PgStat_StatFuncEntry *funcentry;
 
3438
        HASHCTL         hash_ctl;
 
3439
        HTAB       *dbhash;
 
3440
        HTAB       *tabhash = NULL;
 
3441
        HTAB       *funchash = NULL;
 
3442
        FILE       *fpin;
 
3443
        int32           format_id;
 
3444
        bool            found;
 
3445
        const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
 
3446
 
 
3447
        /*
 
3448
         * The tables will live in pgStatLocalContext.
 
3449
         */
 
3450
        pgstat_setup_memcxt();
 
3451
 
 
3452
        /*
 
3453
         * Create the DB hashtable
 
3454
         */
 
3455
        memset(&hash_ctl, 0, sizeof(hash_ctl));
 
3456
        hash_ctl.keysize = sizeof(Oid);
 
3457
        hash_ctl.entrysize = sizeof(PgStat_StatDBEntry);
 
3458
        hash_ctl.hash = oid_hash;
 
3459
        hash_ctl.hcxt = pgStatLocalContext;
 
3460
        dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
 
3461
                                                 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
 
3462
 
 
3463
        /*
 
3464
         * Clear out global statistics so they start from zero in case we can't
 
3465
         * load an existing statsfile.
 
3466
         */
 
3467
        memset(&globalStats, 0, sizeof(globalStats));
 
3468
 
 
3469
        /*
 
3470
         * Set the current timestamp (will be kept only in case we can't load an
 
3471
         * existing statsfile.
 
3472
         */
 
3473
        globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 
3474
 
 
3475
        /*
 
3476
         * Try to open the status file. If it doesn't exist, the backends simply
 
3477
         * return zero for anything and the collector simply starts from scratch
 
3478
         * with empty counters.
 
3479
         *
 
3480
         * ENOENT is a possibility if the stats collector is not running or has
 
3481
         * not yet written the stats file the first time.  Any other failure
 
3482
         * condition is suspicious.
 
3483
         */
 
3484
        if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
 
3485
        {
 
3486
                if (errno != ENOENT)
 
3487
                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3488
                                        (errcode_for_file_access(),
 
3489
                                         errmsg("could not open statistics file \"%s\": %m",
 
3490
                                                        statfile)));
 
3491
                return dbhash;
 
3492
        }
 
3493
 
 
3494
        /*
 
3495
         * Verify it's of the expected format.
 
3496
         */
 
3497
        if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id)
 
3498
                || format_id != PGSTAT_FILE_FORMAT_ID)
 
3499
        {
 
3500
                ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3501
                                (errmsg("corrupted statistics file \"%s\"", statfile)));
 
3502
                goto done;
 
3503
        }
 
3504
 
 
3505
        /*
 
3506
         * Read global stats struct
 
3507
         */
 
3508
        if (fread(&globalStats, 1, sizeof(globalStats), fpin) != sizeof(globalStats))
 
3509
        {
 
3510
                ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3511
                                (errmsg("corrupted statistics file \"%s\"", statfile)));
 
3512
                goto done;
 
3513
        }
 
3514
 
 
3515
        /*
 
3516
         * We found an existing collector stats file. Read it and put all the
 
3517
         * hashtable entries into place.
 
3518
         */
 
3519
        for (;;)
 
3520
        {
 
3521
                switch (fgetc(fpin))
 
3522
                {
 
3523
                                /*
 
3524
                                 * 'D'  A PgStat_StatDBEntry struct describing a database
 
3525
                                 * follows. Subsequently, zero to many 'T' and 'F' entries
 
3526
                                 * will follow until a 'd' is encountered.
 
3527
                                 */
 
3528
                        case 'D':
 
3529
                                if (fread(&dbbuf, 1, offsetof(PgStat_StatDBEntry, tables),
 
3530
                                                  fpin) != offsetof(PgStat_StatDBEntry, tables))
 
3531
                                {
 
3532
                                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3533
                                                        (errmsg("corrupted statistics file \"%s\"",
 
3534
                                                                        statfile)));
 
3535
                                        goto done;
 
3536
                                }
 
3537
 
 
3538
                                /*
 
3539
                                 * Add to the DB hash
 
3540
                                 */
 
3541
                                dbentry = (PgStat_StatDBEntry *) hash_search(dbhash,
 
3542
                                                                                                  (void *) &dbbuf.databaseid,
 
3543
                                                                                                                         HASH_ENTER,
 
3544
                                                                                                                         &found);
 
3545
                                if (found)
 
3546
                                {
 
3547
                                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3548
                                                        (errmsg("corrupted statistics file \"%s\"",
 
3549
                                                                        statfile)));
 
3550
                                        goto done;
 
3551
                                }
 
3552
 
 
3553
                                memcpy(dbentry, &dbbuf, sizeof(PgStat_StatDBEntry));
 
3554
                                dbentry->tables = NULL;
 
3555
                                dbentry->functions = NULL;
 
3556
 
 
3557
                                /*
 
3558
                                 * Don't collect tables if not the requested DB (or the
 
3559
                                 * shared-table info)
 
3560
                                 */
 
3561
                                if (onlydb != InvalidOid)
 
3562
                                {
 
3563
                                        if (dbbuf.databaseid != onlydb &&
 
3564
                                                dbbuf.databaseid != InvalidOid)
 
3565
                                                break;
 
3566
                                }
 
3567
 
 
3568
                                memset(&hash_ctl, 0, sizeof(hash_ctl));
 
3569
                                hash_ctl.keysize = sizeof(Oid);
 
3570
                                hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
 
3571
                                hash_ctl.hash = oid_hash;
 
3572
                                hash_ctl.hcxt = pgStatLocalContext;
 
3573
                                dbentry->tables = hash_create("Per-database table",
 
3574
                                                                                          PGSTAT_TAB_HASH_SIZE,
 
3575
                                                                                          &hash_ctl,
 
3576
                                                                   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
 
3577
 
 
3578
                                hash_ctl.keysize = sizeof(Oid);
 
3579
                                hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
 
3580
                                hash_ctl.hash = oid_hash;
 
3581
                                hash_ctl.hcxt = pgStatLocalContext;
 
3582
                                dbentry->functions = hash_create("Per-database function",
 
3583
                                                                                                 PGSTAT_FUNCTION_HASH_SIZE,
 
3584
                                                                                                 &hash_ctl,
 
3585
                                                                   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
 
3586
 
 
3587
                                /*
 
3588
                                 * Arrange that following records add entries to this
 
3589
                                 * database's hash tables.
 
3590
                                 */
 
3591
                                tabhash = dbentry->tables;
 
3592
                                funchash = dbentry->functions;
 
3593
                                break;
 
3594
 
 
3595
                                /*
 
3596
                                 * 'd'  End of this database.
 
3597
                                 */
 
3598
                        case 'd':
 
3599
                                tabhash = NULL;
 
3600
                                funchash = NULL;
 
3601
                                break;
 
3602
 
 
3603
                                /*
 
3604
                                 * 'T'  A PgStat_StatTabEntry follows.
 
3605
                                 */
 
3606
                        case 'T':
 
3607
                                if (fread(&tabbuf, 1, sizeof(PgStat_StatTabEntry),
 
3608
                                                  fpin) != sizeof(PgStat_StatTabEntry))
 
3609
                                {
 
3610
                                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3611
                                                        (errmsg("corrupted statistics file \"%s\"",
 
3612
                                                                        statfile)));
 
3613
                                        goto done;
 
3614
                                }
 
3615
 
 
3616
                                /*
 
3617
                                 * Skip if table belongs to a not requested database.
 
3618
                                 */
 
3619
                                if (tabhash == NULL)
 
3620
                                        break;
 
3621
 
 
3622
                                tabentry = (PgStat_StatTabEntry *) hash_search(tabhash,
 
3623
                                                                                                        (void *) &tabbuf.tableid,
 
3624
                                                                                                                 HASH_ENTER, &found);
 
3625
 
 
3626
                                if (found)
 
3627
                                {
 
3628
                                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3629
                                                        (errmsg("corrupted statistics file \"%s\"",
 
3630
                                                                        statfile)));
 
3631
                                        goto done;
 
3632
                                }
 
3633
 
 
3634
                                memcpy(tabentry, &tabbuf, sizeof(tabbuf));
 
3635
                                break;
 
3636
 
 
3637
                                /*
 
3638
                                 * 'F'  A PgStat_StatFuncEntry follows.
 
3639
                                 */
 
3640
                        case 'F':
 
3641
                                if (fread(&funcbuf, 1, sizeof(PgStat_StatFuncEntry),
 
3642
                                                  fpin) != sizeof(PgStat_StatFuncEntry))
 
3643
                                {
 
3644
                                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3645
                                                        (errmsg("corrupted statistics file \"%s\"",
 
3646
                                                                        statfile)));
 
3647
                                        goto done;
 
3648
                                }
 
3649
 
 
3650
                                /*
 
3651
                                 * Skip if function belongs to a not requested database.
 
3652
                                 */
 
3653
                                if (funchash == NULL)
 
3654
                                        break;
 
3655
 
 
3656
                                funcentry = (PgStat_StatFuncEntry *) hash_search(funchash,
 
3657
                                                                                                (void *) &funcbuf.functionid,
 
3658
                                                                                                                 HASH_ENTER, &found);
 
3659
 
 
3660
                                if (found)
 
3661
                                {
 
3662
                                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3663
                                                        (errmsg("corrupted statistics file \"%s\"",
 
3664
                                                                        statfile)));
 
3665
                                        goto done;
 
3666
                                }
 
3667
 
 
3668
                                memcpy(funcentry, &funcbuf, sizeof(funcbuf));
 
3669
                                break;
 
3670
 
 
3671
                                /*
 
3672
                                 * 'E'  The EOF marker of a complete stats file.
 
3673
                                 */
 
3674
                        case 'E':
 
3675
                                goto done;
 
3676
 
 
3677
                        default:
 
3678
                                ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3679
                                                (errmsg("corrupted statistics file \"%s\"",
 
3680
                                                                statfile)));
 
3681
                                goto done;
 
3682
                }
 
3683
        }
 
3684
 
 
3685
done:
 
3686
        FreeFile(fpin);
 
3687
 
 
3688
        if (permanent)
 
3689
                unlink(PGSTAT_STAT_PERMANENT_FILENAME);
 
3690
 
 
3691
        return dbhash;
 
3692
}
 
3693
 
 
3694
/* ----------
 
3695
 * pgstat_read_statsfile_timestamp() -
 
3696
 *
 
3697
 *      Attempt to fetch the timestamp of an existing stats file.
 
3698
 *      Returns TRUE if successful (timestamp is stored at *ts).
 
3699
 * ----------
 
3700
 */
 
3701
static bool
 
3702
pgstat_read_statsfile_timestamp(bool permanent, TimestampTz *ts)
 
3703
{
 
3704
        PgStat_GlobalStats myGlobalStats;
 
3705
        FILE       *fpin;
 
3706
        int32           format_id;
 
3707
        const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
 
3708
 
 
3709
        /*
 
3710
         * Try to open the status file.  As above, anything but ENOENT is worthy
 
3711
         * of complaining about.
 
3712
         */
 
3713
        if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
 
3714
        {
 
3715
                if (errno != ENOENT)
 
3716
                        ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3717
                                        (errcode_for_file_access(),
 
3718
                                         errmsg("could not open statistics file \"%s\": %m",
 
3719
                                                        statfile)));
 
3720
                return false;
 
3721
        }
 
3722
 
 
3723
        /*
 
3724
         * Verify it's of the expected format.
 
3725
         */
 
3726
        if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id)
 
3727
                || format_id != PGSTAT_FILE_FORMAT_ID)
 
3728
        {
 
3729
                ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3730
                                (errmsg("corrupted statistics file \"%s\"", statfile)));
 
3731
                FreeFile(fpin);
 
3732
                return false;
 
3733
        }
 
3734
 
 
3735
        /*
 
3736
         * Read global stats struct
 
3737
         */
 
3738
        if (fread(&myGlobalStats, 1, sizeof(myGlobalStats), fpin) != sizeof(myGlobalStats))
 
3739
        {
 
3740
                ereport(pgStatRunningInCollector ? LOG : WARNING,
 
3741
                                (errmsg("corrupted statistics file \"%s\"", statfile)));
 
3742
                FreeFile(fpin);
 
3743
                return false;
 
3744
        }
 
3745
 
 
3746
        *ts = myGlobalStats.stats_timestamp;
 
3747
 
 
3748
        FreeFile(fpin);
 
3749
        return true;
 
3750
}
 
3751
 
 
3752
/*
 
3753
 * If not already done, read the statistics collector stats file into
 
3754
 * some hash tables.  The results will be kept until pgstat_clear_snapshot()
 
3755
 * is called (typically, at end of transaction).
 
3756
 */
 
3757
static void
 
3758
backend_read_statsfile(void)
 
3759
{
 
3760
        TimestampTz min_ts;
 
3761
        int                     count;
 
3762
 
 
3763
        /* already read it? */
 
3764
        if (pgStatDBHash)
 
3765
                return;
 
3766
        Assert(!pgStatRunningInCollector);
 
3767
 
 
3768
        /*
 
3769
         * We set the minimum acceptable timestamp to PGSTAT_STAT_INTERVAL msec
 
3770
         * before now.  This indirectly ensures that the collector needn't write
 
3771
         * the file more often than PGSTAT_STAT_INTERVAL.  In an autovacuum
 
3772
         * worker, however, we want a lower delay to avoid using stale data, so we
 
3773
         * use PGSTAT_RETRY_DELAY (since the number of worker is low, this
 
3774
         * shouldn't be a problem).
 
3775
         *
 
3776
         * Note that we don't recompute min_ts after sleeping; so we might end up
 
3777
         * accepting a file a bit older than PGSTAT_STAT_INTERVAL.      In practice
 
3778
         * that shouldn't happen, though, as long as the sleep time is less than
 
3779
         * PGSTAT_STAT_INTERVAL; and we don't want to lie to the collector about
 
3780
         * what our cutoff time really is.
 
3781
         */
 
3782
        if (IsAutoVacuumWorkerProcess())
 
3783
                min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
 
3784
                                                                                         -PGSTAT_RETRY_DELAY);
 
3785
        else
 
3786
                min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
 
3787
                                                                                         -PGSTAT_STAT_INTERVAL);
 
3788
 
 
3789
        /*
 
3790
         * Loop until fresh enough stats file is available or we ran out of time.
 
3791
         * The stats inquiry message is sent repeatedly in case collector drops
 
3792
         * it.
 
3793
         */
 
3794
        for (count = 0; count < PGSTAT_POLL_LOOP_COUNT; count++)
 
3795
        {
 
3796
                TimestampTz file_ts = 0;
 
3797
 
 
3798
                CHECK_FOR_INTERRUPTS();
 
3799
 
 
3800
                if (pgstat_read_statsfile_timestamp(false, &file_ts) &&
 
3801
                        file_ts >= min_ts)
 
3802
                        break;
 
3803
 
 
3804
                /* Not there or too old, so kick the collector and wait a bit */
 
3805
                pgstat_send_inquiry(min_ts);
 
3806
                pg_usleep(PGSTAT_RETRY_DELAY * 1000L);
 
3807
        }
 
3808
 
 
3809
        if (count >= PGSTAT_POLL_LOOP_COUNT)
 
3810
                elog(WARNING, "pgstat wait timeout");
 
3811
 
 
3812
        /* Autovacuum launcher wants stats about all databases */
 
3813
        if (IsAutoVacuumLauncherProcess())
 
3814
                pgStatDBHash = pgstat_read_statsfile(InvalidOid, false);
 
3815
        else
 
3816
                pgStatDBHash = pgstat_read_statsfile(MyDatabaseId, false);
 
3817
}
 
3818
 
 
3819
 
 
3820
/* ----------
 
3821
 * pgstat_setup_memcxt() -
 
3822
 *
 
3823
 *      Create pgStatLocalContext, if not already done.
 
3824
 * ----------
 
3825
 */
 
3826
static void
 
3827
pgstat_setup_memcxt(void)
 
3828
{
 
3829
        if (!pgStatLocalContext)
 
3830
                pgStatLocalContext = AllocSetContextCreate(TopMemoryContext,
 
3831
                                                                                                   "Statistics snapshot",
 
3832
                                                                                                   ALLOCSET_SMALL_MINSIZE,
 
3833
                                                                                                   ALLOCSET_SMALL_INITSIZE,
 
3834
                                                                                                   ALLOCSET_SMALL_MAXSIZE);
 
3835
}
 
3836
 
 
3837
 
 
3838
/* ----------
 
3839
 * pgstat_clear_snapshot() -
 
3840
 *
 
3841
 *      Discard any data collected in the current transaction.  Any subsequent
 
3842
 *      request will cause new snapshots to be read.
 
3843
 *
 
3844
 *      This is also invoked during transaction commit or abort to discard
 
3845
 *      the no-longer-wanted snapshot.
 
3846
 * ----------
 
3847
 */
 
3848
void
 
3849
pgstat_clear_snapshot(void)
 
3850
{
 
3851
        /* Release memory, if any was allocated */
 
3852
        if (pgStatLocalContext)
 
3853
                MemoryContextDelete(pgStatLocalContext);
 
3854
 
 
3855
        /* Reset variables */
 
3856
        pgStatLocalContext = NULL;
 
3857
        pgStatDBHash = NULL;
 
3858
        localBackendStatusTable = NULL;
 
3859
        localNumBackends = 0;
 
3860
}
 
3861
 
 
3862
 
 
3863
/* ----------
 
3864
 * pgstat_recv_inquiry() -
 
3865
 *
 
3866
 *      Process stat inquiry requests.
 
3867
 * ----------
 
3868
 */
 
3869
static void
 
3870
pgstat_recv_inquiry(PgStat_MsgInquiry *msg, int len)
 
3871
{
 
3872
        if (msg->inquiry_time > last_statrequest)
 
3873
                last_statrequest = msg->inquiry_time;
 
3874
}
 
3875
 
 
3876
 
 
3877
/* ----------
 
3878
 * pgstat_recv_tabstat() -
 
3879
 *
 
3880
 *      Count what the backend has done.
 
3881
 * ----------
 
3882
 */
 
3883
static void
 
3884
pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
 
3885
{
 
3886
        PgStat_StatDBEntry *dbentry;
 
3887
        PgStat_StatTabEntry *tabentry;
 
3888
        int                     i;
 
3889
        bool            found;
 
3890
 
 
3891
        dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
 
3892
 
 
3893
        /*
 
3894
         * Update database-wide stats.
 
3895
         */
 
3896
        dbentry->n_xact_commit += (PgStat_Counter) (msg->m_xact_commit);
 
3897
        dbentry->n_xact_rollback += (PgStat_Counter) (msg->m_xact_rollback);
 
3898
 
 
3899
        /*
 
3900
         * Process all table entries in the message.
 
3901
         */
 
3902
        for (i = 0; i < msg->m_nentries; i++)
 
3903
        {
 
3904
                PgStat_TableEntry *tabmsg = &(msg->m_entry[i]);
 
3905
 
 
3906
                tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
 
3907
                                                                                                        (void *) &(tabmsg->t_id),
 
3908
                                                                                                           HASH_ENTER, &found);
 
3909
 
 
3910
                if (!found)
 
3911
                {
 
3912
                        /*
 
3913
                         * If it's a new table entry, initialize counters to the values we
 
3914
                         * just got.
 
3915
                         */
 
3916
                        tabentry->numscans = tabmsg->t_counts.t_numscans;
 
3917
                        tabentry->tuples_returned = tabmsg->t_counts.t_tuples_returned;
 
3918
                        tabentry->tuples_fetched = tabmsg->t_counts.t_tuples_fetched;
 
3919
                        tabentry->tuples_inserted = tabmsg->t_counts.t_tuples_inserted;
 
3920
                        tabentry->tuples_updated = tabmsg->t_counts.t_tuples_updated;
 
3921
                        tabentry->tuples_deleted = tabmsg->t_counts.t_tuples_deleted;
 
3922
                        tabentry->tuples_hot_updated = tabmsg->t_counts.t_tuples_hot_updated;
 
3923
                        tabentry->n_live_tuples = tabmsg->t_counts.t_delta_live_tuples;
 
3924
                        tabentry->n_dead_tuples = tabmsg->t_counts.t_delta_dead_tuples;
 
3925
                        tabentry->changes_since_analyze = tabmsg->t_counts.t_changed_tuples;
 
3926
                        tabentry->blocks_fetched = tabmsg->t_counts.t_blocks_fetched;
 
3927
                        tabentry->blocks_hit = tabmsg->t_counts.t_blocks_hit;
 
3928
 
 
3929
                        tabentry->vacuum_timestamp = 0;
 
3930
                        tabentry->vacuum_count = 0;
 
3931
                        tabentry->autovac_vacuum_timestamp = 0;
 
3932
                        tabentry->autovac_vacuum_count = 0;
 
3933
                        tabentry->analyze_timestamp = 0;
 
3934
                        tabentry->analyze_count = 0;
 
3935
                        tabentry->autovac_analyze_timestamp = 0;
 
3936
                        tabentry->autovac_analyze_count = 0;
 
3937
                }
 
3938
                else
 
3939
                {
 
3940
                        /*
 
3941
                         * Otherwise add the values to the existing entry.
 
3942
                         */
 
3943
                        tabentry->numscans += tabmsg->t_counts.t_numscans;
 
3944
                        tabentry->tuples_returned += tabmsg->t_counts.t_tuples_returned;
 
3945
                        tabentry->tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
 
3946
                        tabentry->tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
 
3947
                        tabentry->tuples_updated += tabmsg->t_counts.t_tuples_updated;
 
3948
                        tabentry->tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
 
3949
                        tabentry->tuples_hot_updated += tabmsg->t_counts.t_tuples_hot_updated;
 
3950
                        tabentry->n_live_tuples += tabmsg->t_counts.t_delta_live_tuples;
 
3951
                        tabentry->n_dead_tuples += tabmsg->t_counts.t_delta_dead_tuples;
 
3952
                        tabentry->changes_since_analyze += tabmsg->t_counts.t_changed_tuples;
 
3953
                        tabentry->blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
 
3954
                        tabentry->blocks_hit += tabmsg->t_counts.t_blocks_hit;
 
3955
                }
 
3956
 
 
3957
                /* Clamp n_live_tuples in case of negative delta_live_tuples */
 
3958
                tabentry->n_live_tuples = Max(tabentry->n_live_tuples, 0);
 
3959
                /* Likewise for n_dead_tuples */
 
3960
                tabentry->n_dead_tuples = Max(tabentry->n_dead_tuples, 0);
 
3961
 
 
3962
                /*
 
3963
                 * Add per-table stats to the per-database entry, too.
 
3964
                 */
 
3965
                dbentry->n_tuples_returned += tabmsg->t_counts.t_tuples_returned;
 
3966
                dbentry->n_tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
 
3967
                dbentry->n_tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
 
3968
                dbentry->n_tuples_updated += tabmsg->t_counts.t_tuples_updated;
 
3969
                dbentry->n_tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
 
3970
                dbentry->n_blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
 
3971
                dbentry->n_blocks_hit += tabmsg->t_counts.t_blocks_hit;
 
3972
        }
 
3973
}
 
3974
 
 
3975
 
 
3976
/* ----------
 
3977
 * pgstat_recv_tabpurge() -
 
3978
 *
 
3979
 *      Arrange for dead table removal.
 
3980
 * ----------
 
3981
 */
 
3982
static void
 
3983
pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len)
 
3984
{
 
3985
        PgStat_StatDBEntry *dbentry;
 
3986
        int                     i;
 
3987
 
 
3988
        dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
 
3989
 
 
3990
        /*
 
3991
         * No need to purge if we don't even know the database.
 
3992
         */
 
3993
        if (!dbentry || !dbentry->tables)
 
3994
                return;
 
3995
 
 
3996
        /*
 
3997
         * Process all table entries in the message.
 
3998
         */
 
3999
        for (i = 0; i < msg->m_nentries; i++)
 
4000
        {
 
4001
                /* Remove from hashtable if present; we don't care if it's not. */
 
4002
                (void) hash_search(dbentry->tables,
 
4003
                                                   (void *) &(msg->m_tableid[i]),
 
4004
                                                   HASH_REMOVE, NULL);
 
4005
        }
 
4006
}
 
4007
 
 
4008
 
 
4009
/* ----------
 
4010
 * pgstat_recv_dropdb() -
 
4011
 *
 
4012
 *      Arrange for dead database removal
 
4013
 * ----------
 
4014
 */
 
4015
static void
 
4016
pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len)
 
4017
{
 
4018
        PgStat_StatDBEntry *dbentry;
 
4019
 
 
4020
        /*
 
4021
         * Lookup the database in the hashtable.
 
4022
         */
 
4023
        dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
 
4024
 
 
4025
        /*
 
4026
         * If found, remove it.
 
4027
         */
 
4028
        if (dbentry)
 
4029
        {
 
4030
                if (dbentry->tables != NULL)
 
4031
                        hash_destroy(dbentry->tables);
 
4032
                if (dbentry->functions != NULL)
 
4033
                        hash_destroy(dbentry->functions);
 
4034
 
 
4035
                if (hash_search(pgStatDBHash,
 
4036
                                                (void *) &(dbentry->databaseid),
 
4037
                                                HASH_REMOVE, NULL) == NULL)
 
4038
                        ereport(ERROR,
 
4039
                                        (errmsg("database hash table corrupted "
 
4040
                                                        "during cleanup --- abort")));
 
4041
        }
 
4042
}
 
4043
 
 
4044
 
 
4045
/* ----------
 
4046
 * pgstat_recv_resetcounter() -
 
4047
 *
 
4048
 *      Reset the statistics for the specified database.
 
4049
 * ----------
 
4050
 */
 
4051
static void
 
4052
pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len)
 
4053
{
 
4054
        HASHCTL         hash_ctl;
 
4055
        PgStat_StatDBEntry *dbentry;
 
4056
 
 
4057
        /*
 
4058
         * Lookup the database in the hashtable.  Nothing to do if not there.
 
4059
         */
 
4060
        dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
 
4061
 
 
4062
        if (!dbentry)
 
4063
                return;
 
4064
 
 
4065
        /*
 
4066
         * We simply throw away all the database's table entries by recreating a
 
4067
         * new hash table for them.
 
4068
         */
 
4069
        if (dbentry->tables != NULL)
 
4070
                hash_destroy(dbentry->tables);
 
4071
        if (dbentry->functions != NULL)
 
4072
                hash_destroy(dbentry->functions);
 
4073
 
 
4074
        dbentry->tables = NULL;
 
4075
        dbentry->functions = NULL;
 
4076
 
 
4077
        /*
 
4078
         * Reset database-level stats too.      This should match the initialization
 
4079
         * code in pgstat_get_db_entry().
 
4080
         */
 
4081
        dbentry->n_xact_commit = 0;
 
4082
        dbentry->n_xact_rollback = 0;
 
4083
        dbentry->n_blocks_fetched = 0;
 
4084
        dbentry->n_blocks_hit = 0;
 
4085
        dbentry->n_tuples_returned = 0;
 
4086
        dbentry->n_tuples_fetched = 0;
 
4087
        dbentry->n_tuples_inserted = 0;
 
4088
        dbentry->n_tuples_updated = 0;
 
4089
        dbentry->n_tuples_deleted = 0;
 
4090
        dbentry->last_autovac_time = 0;
 
4091
 
 
4092
        dbentry->stat_reset_timestamp = GetCurrentTimestamp();
 
4093
 
 
4094
        memset(&hash_ctl, 0, sizeof(hash_ctl));
 
4095
        hash_ctl.keysize = sizeof(Oid);
 
4096
        hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
 
4097
        hash_ctl.hash = oid_hash;
 
4098
        dbentry->tables = hash_create("Per-database table",
 
4099
                                                                  PGSTAT_TAB_HASH_SIZE,
 
4100
                                                                  &hash_ctl,
 
4101
                                                                  HASH_ELEM | HASH_FUNCTION);
 
4102
 
 
4103
        hash_ctl.keysize = sizeof(Oid);
 
4104
        hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
 
4105
        hash_ctl.hash = oid_hash;
 
4106
        dbentry->functions = hash_create("Per-database function",
 
4107
                                                                         PGSTAT_FUNCTION_HASH_SIZE,
 
4108
                                                                         &hash_ctl,
 
4109
                                                                         HASH_ELEM | HASH_FUNCTION);
 
4110
}
 
4111
 
 
4112
/* ----------
 
4113
 * pgstat_recv_resetshared() -
 
4114
 *
 
4115
 *      Reset some shared statistics of the cluster.
 
4116
 * ----------
 
4117
 */
 
4118
static void
 
4119
pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 
4120
{
 
4121
        if (msg->m_resettarget == RESET_BGWRITER)
 
4122
        {
 
4123
                /* Reset the global background writer statistics for the cluster. */
 
4124
                memset(&globalStats, 0, sizeof(globalStats));
 
4125
                globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 
4126
        }
 
4127
 
 
4128
        /*
 
4129
         * Presumably the sender of this message validated the target, don't
 
4130
         * complain here if it's not valid
 
4131
         */
 
4132
}
 
4133
 
 
4134
/* ----------
 
4135
 * pgstat_recv_resetsinglecounter() -
 
4136
 *
 
4137
 *      Reset a statistics for a single object
 
4138
 * ----------
 
4139
 */
 
4140
static void
 
4141
pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len)
 
4142
{
 
4143
        PgStat_StatDBEntry *dbentry;
 
4144
 
 
4145
        dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
 
4146
 
 
4147
        if (!dbentry)
 
4148
                return;
 
4149
 
 
4150
        /* Set the reset timestamp for the whole database */
 
4151
        dbentry->stat_reset_timestamp = GetCurrentTimestamp();
 
4152
 
 
4153
        /* Remove object if it exists, ignore it if not */
 
4154
        if (msg->m_resettype == RESET_TABLE)
 
4155
                (void) hash_search(dbentry->tables, (void *) &(msg->m_objectid),
 
4156
                                                   HASH_REMOVE, NULL);
 
4157
        else if (msg->m_resettype == RESET_FUNCTION)
 
4158
                (void) hash_search(dbentry->functions, (void *) &(msg->m_objectid),
 
4159
                                                   HASH_REMOVE, NULL);
 
4160
}
 
4161
 
 
4162
/* ----------
 
4163
 * pgstat_recv_autovac() -
 
4164
 *
 
4165
 *      Process an autovacuum signalling message.
 
4166
 * ----------
 
4167
 */
 
4168
static void
 
4169
pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len)
 
4170
{
 
4171
        PgStat_StatDBEntry *dbentry;
 
4172
 
 
4173
        /*
 
4174
         * Store the last autovacuum time in the database's hashtable entry.
 
4175
         */
 
4176
        dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
 
4177
 
 
4178
        dbentry->last_autovac_time = msg->m_start_time;
 
4179
}
 
4180
 
 
4181
/* ----------
 
4182
 * pgstat_recv_vacuum() -
 
4183
 *
 
4184
 *      Process a VACUUM message.
 
4185
 * ----------
 
4186
 */
 
4187
static void
 
4188
pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len)
 
4189
{
 
4190
        PgStat_StatDBEntry *dbentry;
 
4191
        PgStat_StatTabEntry *tabentry;
 
4192
 
 
4193
        /*
 
4194
         * Store the data in the table's hashtable entry.
 
4195
         */
 
4196
        dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
 
4197
 
 
4198
        tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
 
4199
 
 
4200
        if (msg->m_adopt_counts)
 
4201
                tabentry->n_live_tuples = msg->m_tuples;
 
4202
        /* Resetting dead_tuples to 0 is an approximation ... */
 
4203
        tabentry->n_dead_tuples = 0;
 
4204
 
 
4205
        if (msg->m_autovacuum)
 
4206
        {
 
4207
                tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
 
4208
                tabentry->autovac_vacuum_count++;
 
4209
        }
 
4210
        else
 
4211
        {
 
4212
                tabentry->vacuum_timestamp = msg->m_vacuumtime;
 
4213
                tabentry->vacuum_count++;
 
4214
        }
 
4215
}
 
4216
 
 
4217
/* ----------
 
4218
 * pgstat_recv_analyze() -
 
4219
 *
 
4220
 *      Process an ANALYZE message.
 
4221
 * ----------
 
4222
 */
 
4223
static void
 
4224
pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
 
4225
{
 
4226
        PgStat_StatDBEntry *dbentry;
 
4227
        PgStat_StatTabEntry *tabentry;
 
4228
 
 
4229
        /*
 
4230
         * Store the data in the table's hashtable entry.
 
4231
         */
 
4232
        dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
 
4233
 
 
4234
        tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
 
4235
 
 
4236
        if (msg->m_adopt_counts)
 
4237
        {
 
4238
                tabentry->n_live_tuples = msg->m_live_tuples;
 
4239
                tabentry->n_dead_tuples = msg->m_dead_tuples;
 
4240
        }
 
4241
 
 
4242
        /*
 
4243
         * We reset changes_since_analyze to zero, forgetting any changes that
 
4244
         * occurred while the ANALYZE was in progress.
 
4245
         */
 
4246
        tabentry->changes_since_analyze = 0;
 
4247
 
 
4248
        if (msg->m_autovacuum)
 
4249
        {
 
4250
                tabentry->autovac_analyze_timestamp = msg->m_analyzetime;
 
4251
                tabentry->autovac_analyze_count++;
 
4252
        }
 
4253
        else
 
4254
        {
 
4255
                tabentry->analyze_timestamp = msg->m_analyzetime;
 
4256
                tabentry->analyze_count++;
 
4257
        }
 
4258
}
 
4259
 
 
4260
 
 
4261
/* ----------
 
4262
 * pgstat_recv_bgwriter() -
 
4263
 *
 
4264
 *      Process a BGWRITER message.
 
4265
 * ----------
 
4266
 */
 
4267
static void
 
4268
pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 
4269
{
 
4270
        globalStats.timed_checkpoints += msg->m_timed_checkpoints;
 
4271
        globalStats.requested_checkpoints += msg->m_requested_checkpoints;
 
4272
        globalStats.buf_written_checkpoints += msg->m_buf_written_checkpoints;
 
4273
        globalStats.buf_written_clean += msg->m_buf_written_clean;
 
4274
        globalStats.maxwritten_clean += msg->m_maxwritten_clean;
 
4275
        globalStats.buf_written_backend += msg->m_buf_written_backend;
 
4276
        globalStats.buf_fsync_backend += msg->m_buf_fsync_backend;
 
4277
        globalStats.buf_alloc += msg->m_buf_alloc;
 
4278
}
 
4279
 
 
4280
/* ----------
 
4281
 * pgstat_recv_recoveryconflict() -
 
4282
 *
 
4283
 *      Process as RECOVERYCONFLICT message.
 
4284
 * ----------
 
4285
 */
 
4286
static void
 
4287
pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len)
 
4288
{
 
4289
        PgStat_StatDBEntry *dbentry;
 
4290
 
 
4291
        dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
 
4292
 
 
4293
        switch (msg->m_reason)
 
4294
        {
 
4295
                case PROCSIG_RECOVERY_CONFLICT_DATABASE:
 
4296
 
 
4297
                        /*
 
4298
                         * Since we drop the information about the database as soon as it
 
4299
                         * replicates, there is no point in counting these conflicts.
 
4300
                         */
 
4301
                        break;
 
4302
                case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
 
4303
                        dbentry->n_conflict_tablespace++;
 
4304
                        break;
 
4305
                case PROCSIG_RECOVERY_CONFLICT_LOCK:
 
4306
                        dbentry->n_conflict_lock++;
 
4307
                        break;
 
4308
                case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
 
4309
                        dbentry->n_conflict_snapshot++;
 
4310
                        break;
 
4311
                case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
 
4312
                        dbentry->n_conflict_bufferpin++;
 
4313
                        break;
 
4314
                case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
 
4315
                        dbentry->n_conflict_startup_deadlock++;
 
4316
                        break;
 
4317
        }
 
4318
}
 
4319
 
 
4320
/* ----------
 
4321
 * pgstat_recv_funcstat() -
 
4322
 *
 
4323
 *      Count what the backend has done.
 
4324
 * ----------
 
4325
 */
 
4326
static void
 
4327
pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len)
 
4328
{
 
4329
        PgStat_FunctionEntry *funcmsg = &(msg->m_entry[0]);
 
4330
        PgStat_StatDBEntry *dbentry;
 
4331
        PgStat_StatFuncEntry *funcentry;
 
4332
        int                     i;
 
4333
        bool            found;
 
4334
 
 
4335
        dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
 
4336
 
 
4337
        /*
 
4338
         * Process all function entries in the message.
 
4339
         */
 
4340
        for (i = 0; i < msg->m_nentries; i++, funcmsg++)
 
4341
        {
 
4342
                funcentry = (PgStat_StatFuncEntry *) hash_search(dbentry->functions,
 
4343
                                                                                                   (void *) &(funcmsg->f_id),
 
4344
                                                                                                                 HASH_ENTER, &found);
 
4345
 
 
4346
                if (!found)
 
4347
                {
 
4348
                        /*
 
4349
                         * If it's a new function entry, initialize counters to the values
 
4350
                         * we just got.
 
4351
                         */
 
4352
                        funcentry->f_numcalls = funcmsg->f_numcalls;
 
4353
                        funcentry->f_time = funcmsg->f_time;
 
4354
                        funcentry->f_time_self = funcmsg->f_time_self;
 
4355
                }
 
4356
                else
 
4357
                {
 
4358
                        /*
 
4359
                         * Otherwise add the values to the existing entry.
 
4360
                         */
 
4361
                        funcentry->f_numcalls += funcmsg->f_numcalls;
 
4362
                        funcentry->f_time += funcmsg->f_time;
 
4363
                        funcentry->f_time_self += funcmsg->f_time_self;
 
4364
                }
 
4365
        }
 
4366
}
 
4367
 
 
4368
/* ----------
 
4369
 * pgstat_recv_funcpurge() -
 
4370
 *
 
4371
 *      Arrange for dead function removal.
 
4372
 * ----------
 
4373
 */
 
4374
static void
 
4375
pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len)
 
4376
{
 
4377
        PgStat_StatDBEntry *dbentry;
 
4378
        int                     i;
 
4379
 
 
4380
        dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
 
4381
 
 
4382
        /*
 
4383
         * No need to purge if we don't even know the database.
 
4384
         */
 
4385
        if (!dbentry || !dbentry->functions)
 
4386
                return;
 
4387
 
 
4388
        /*
 
4389
         * Process all function entries in the message.
 
4390
         */
 
4391
        for (i = 0; i < msg->m_nentries; i++)
 
4392
        {
 
4393
                /* Remove from hashtable if present; we don't care if it's not. */
 
4394
                (void) hash_search(dbentry->functions,
 
4395
                                                   (void *) &(msg->m_functionid[i]),
 
4396
                                                   HASH_REMOVE, NULL);
 
4397
        }
 
4398
}