summaryrefslogtreecommitdiffstats
path: root/machine/m_common.c
blob: c8cf952dc0dfadd7bd23f2b960a90d3ca7b0fc2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
 * machine/m_common.c
 *
 * Functionalities common to all the platforms.
 *
 * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
 */
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <libpq-fe.h>

#include "machine.h"

/* Query to fetch information about database activity */
#define QUERY_STAT_DB \
		"SELECT datid, datname, numbackends, xact_commit, xact_rollback, \n" \
		"       blks_read, blks_hit, tup_returned, tup_fetched, \n" \
		"       tup_inserted, tup_updated, tup_deleted, conflicts \n" \
		"FROM pg_stat_database;"

#define QUERY_DATA_DIRECTORY "SHOW data_directory;"

char *backendstatenames[] =
{
	"", "idle", "active", "idltxn", "fast", "abort", "disabl", NULL
};


/* Store data directory to avoid unnecessary requests to server */
static char *data_directory = NULL;

/*
 * Get database info via the above QUERY_STAT_DB info.
 * Returns rate info on the various statistics by comparing current
 * values with previous values.
 */
void
get_database_info(struct db_info *db_info, const char *values[])
{
	struct timeval thistime;
	double		timediff;
	int			i;
	int			rows;
	PGconn	   *pgconn;
	PGresult   *pgresult = NULL;
	struct db_info cur_info;
	static struct timeval lasttime;
	static struct db_info last_db_info;

	/* calculate the time difference since our last check */
	gettimeofday(&thistime, 0);
	if (lasttime.tv_sec)
		timediff = ((thistime.tv_sec - lasttime.tv_sec) +
					(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
	else
		timediff = 0;

	lasttime = thistime;

	rows = 0;
	pgconn = connect_to_db(values);
	if (pgconn != NULL)
	{
		pgresult = PQexec(pgconn, QUERY_STAT_DB);
		if (PQresultStatus(pgresult) == PGRES_TUPLES_OK)
			rows = PQntuples(pgresult);

	}
	if (rows == 0)
	{
		/* Database probably stopped, clear current and last */
		memset(&last_db_info, 0, sizeof(last_db_info));
	}
	memset(&cur_info, 0, sizeof(cur_info));
	for (i = 0; i < rows; i++)
	{
		PQgetvalue(pgresult, i, 2);
		/* Count all databases, even with no active backends */
		cur_info.numDb++;
		cur_info.numXact += atoi(PQgetvalue(pgresult, i, 3));
		cur_info.numRollback += atoi(PQgetvalue(pgresult, i, 4));
		cur_info.numBlockRead += atoi(PQgetvalue(pgresult, i, 5));
		cur_info.numBlockHit += atoi(PQgetvalue(pgresult, i, 6));
		cur_info.numTupleFetched += atoi(PQgetvalue(pgresult, i, 8));
		cur_info.numTupleAltered += atoi(PQgetvalue(pgresult, i, 9)) +
			atoi(PQgetvalue(pgresult, i, 10)) +
			atoi(PQgetvalue(pgresult, i, 11));
		cur_info.numConflict += atoi(PQgetvalue(pgresult, i, 12));
	}
	if (pgresult != NULL)
		PQclear(pgresult);
	PQfinish(pgconn);
	if (timediff <= 0)
	{
		last_db_info = cur_info;
		memset(db_info, 0, sizeof(*db_info));
		return;
	}

	/* Compute the rate information */
	db_info->numDb = cur_info.numDb;
	db_info->numXact = (double)(cur_info.numXact - last_db_info.numXact) / timediff;
	db_info->numRollback = (double)(cur_info.numRollback - last_db_info.numRollback) / timediff;
	db_info->numBlockRead = (double)(cur_info.numBlockRead - last_db_info.numBlockRead) / timediff;
	db_info->numBlockHit = (double)(cur_info.numBlockHit - last_db_info.numBlockHit) / timediff;
	db_info->numTupleFetched = (double)(cur_info.numTupleFetched - last_db_info.numTupleFetched) / timediff;
	db_info->numTupleAltered = (double)(cur_info.numTupleAltered - last_db_info.numTupleAltered) / timediff;
	db_info->numConflict = (double)(cur_info.numConflict - last_db_info.numConflict) / timediff;
	last_db_info = cur_info;
}

/*
 * Obtain data directory of server if necessary. if this has already been
 * queried to server, return existing value.
 */
char *
get_data_directory(const char *values[])
{
	PGconn	   *pgconn;
	PGresult   *pgresult = NULL;
	int			rows;

	/* Return existing value if any */
	if (data_directory)
		return data_directory;

	/* No existing value, so query server */
	rows = 0;
	pgconn = connect_to_db(values);
	if (pgconn != NULL)
	{
		pgresult = PQexec(pgconn, QUERY_DATA_DIRECTORY);
		if (PQresultStatus(pgresult) == PGRES_TUPLES_OK)
			rows = PQntuples(pgresult);
	}

	if (rows != 0)
	{
		char *dir;
		dir = PQgetvalue(pgresult, 0, 0);
		if (dir != NULL)
			data_directory = strdup(dir);
	}

	/* Clean up */
	if (pgresult != NULL)
		PQclear(pgresult);
	PQfinish(pgconn);

	return data_directory;
}

void
update_state(int *pgstate, char *state)
{
	if (strcmp(state, "idle") == 0)
		*pgstate = STATE_IDLE;
	else if (strcmp(state, "active") == 0)
		*pgstate = STATE_RUNNING;
	else if (strcmp(state, "idle in transaction") == 0)
		*pgstate = STATE_IDLEINTRANSACTION;
	else if (strcmp(state, "fastpath function call") == 0)
		*pgstate = STATE_FASTPATH;
	else if (strcmp(state, "idle in transaction (aborted)") == 0)
		*pgstate = STATE_IDLEINTRANSACTION_ABORTED;
	else if (strcmp(state, "disabled") == 0)
		*pgstate = STATE_DISABLED;
	else
		*pgstate = STATE_UNDEFINED;
}