1 #include "pgreplay.h"
2 
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <assert.h>
7 #include <sys/time.h>
8 
9 /* type for one replay line */
10 struct replay_item {
11 	struct timeval time;
12 	uint64_t session_id;
13 	replay_type type;
14 	uint16_t count;
15 	char **data;
16 };
17 
18 /* special replay_item that signals end-of-file */
19 static replay_item end_replay_item = {{0, 0}, 0, -1, 0, NULL};
20 replay_item * const end_item = &end_replay_item;
21 
22 /* create common part of a replay_item */
replay_create(const struct timeval * time,uint64_t session_id,replay_type type,uint16_t count)23 static replay_item *replay_create(const struct timeval *time, uint64_t session_id, replay_type type, uint16_t count) {
24 	replay_item *r;
25 
26 	r = malloc(sizeof(struct replay_item));
27 	if (NULL == r) {
28 		fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)sizeof(struct replay_item));
29 		return NULL;
30 	}
31 	r->time.tv_sec  = time->tv_sec;
32 	r->time.tv_usec = time->tv_usec;
33 	r->session_id   = session_id;
34 	r->type         = type;
35 	r->count        = count;
36 	if (0 == count) {
37 		r->data = NULL;
38 	} else {
39 		r->data = calloc(count, sizeof(char *));
40 		if (NULL == r->data) {
41 			fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)count * sizeof(char *));
42 			free(r);
43 			return NULL;
44 		}
45 	}
46 
47 	return r;
48 }
49 
replay_create_connect(const struct timeval * time,uint64_t session_id,const char * user,const char * database)50 replay_item *replay_create_connect(const struct timeval *time, uint64_t session_id, const char *user, const char *database) {
51 	replay_item *r;
52 
53 	debug(3, "Entering replay_create_connect%s\n", "");
54 
55 	r = replay_create(time, session_id, pg_connect, 2);
56 	if (NULL == r) {
57 		return NULL;
58 	}
59 
60 	(r->data)[0] = malloc(strlen(user) + 1);
61 	if (NULL == (r->data)[0]) {
62 		fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)strlen(user) + 1);
63 		free(r->data);
64 		free(r);
65 		return NULL;
66 	}
67 	strcpy((r->data)[0], user);
68 
69 	(r->data)[1] = malloc(strlen(database) + 1);
70 	if (NULL == (r->data)[1]) {
71 		fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)strlen(user) + 1);
72 		free((r->data)[0]);
73 		free(r->data);
74 		free(r);
75 		return NULL;
76 	}
77 	strcpy((r->data)[1], database);
78 
79 	debug(3, "Leaving replay_create_connect%s\n", "");
80 	return r;
81 }
82 
replay_create_disconnect(const struct timeval * time,uint64_t session_id)83 replay_item *replay_create_disconnect(const struct timeval *time, uint64_t session_id) {
84 	replay_item *r;
85 
86 	debug(3, "Entering replay_create_disconnect%s\n", "");
87 
88 	r = replay_create(time, session_id, pg_disconnect, 0);
89 	if (NULL == r) {
90 		return NULL;
91 	}
92 	debug(3, "Leaving replay_create_disconnect%s\n", "");
93 	return r;
94 }
95 
replay_create_execute(const struct timeval * time,uint64_t session_id,const char * statement)96 replay_item *replay_create_execute(const struct timeval *time, uint64_t session_id, const char *statement) {
97 	replay_item *r;
98 
99 	debug(3, "Entering replay_create_execute%s\n", "");
100 
101 	r = replay_create(time, session_id, pg_execute, 1);
102 	if (NULL == r) {
103 		return NULL;
104 	}
105 
106 	(r->data)[0] = malloc(strlen(statement) + 1);
107 	if (NULL == (r->data)[0]) {
108 		fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)strlen(statement) + 1);
109 		free(r->data);
110 		free(r);
111 		return NULL;
112 	}
113 	strcpy((r->data)[0], statement);
114 
115 	debug(3, "Leaving replay_create_execute%s\n", "");
116 	return r;
117 }
118 
replay_create_prepare(const struct timeval * time,uint64_t session_id,const char * name,const char * statement)119 replay_item *replay_create_prepare(const struct timeval *time, uint64_t session_id, const char *name, const char *statement) {
120 	replay_item *r;
121 
122 	debug(3, "Entering replay_create_prepare%s\n", "");
123 
124 	r = replay_create(time, session_id, pg_prepare, 2);
125 	if (NULL == r) {
126 		return NULL;
127 	}
128 
129 	(r->data)[0] = malloc(strlen(statement) + 1);
130 	if (NULL == (r->data)[0]) {
131 		fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)strlen(statement) + 1);
132 		free(r->data);
133 		free(r);
134 		return NULL;
135 	}
136 	strcpy((r->data)[0], statement);
137 
138 	(r->data)[1] = malloc(strlen(name) + 1);
139 	if (NULL == (r->data)[1]) {
140 		fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)strlen(name) + 1);
141 		free((r->data)[0]);
142 		free(r->data);
143 		free(r);
144 		return NULL;
145 	}
146 	strcpy((r->data)[1], name);
147 
148 	debug(3, "Leaving replay_create_prepare%s\n", "");
149 	return r;
150 }
151 
replay_create_exec_prepared(const struct timeval * time,uint64_t session_id,const char * name,uint16_t count,char * const * values)152 replay_item *replay_create_exec_prepared(const struct timeval *time, uint64_t session_id, const char*name, uint16_t count, char * const *values) {
153 	replay_item *r;
154 	int i;
155 
156 	debug(3, "Entering replay_create_exec_prepared%s\n", "");
157 
158 	r = replay_create(time, session_id, pg_exec_prepared, count + 1);
159 	if (NULL == r) {
160 		return NULL;
161 	}
162 
163 	(r->data)[0] = malloc(strlen(name) + 1);
164 	if (NULL == (r->data)[0]) {
165 		free(r->data);
166 		free(r);
167 		return NULL;
168 	}
169 	strcpy((r->data)[0], name);
170 
171 	for (i=1; i<count+1; ++i) {
172 		if (values[i-1]) {
173 			(r->data)[i] = malloc(strlen(values[i-1]) + 1);
174 			if (NULL == (r->data)[i]) {
175 				fprintf(stderr, "Cannot allocate %lu bytes of memory\n", (unsigned long)strlen(values[i-1]) + 1);
176 				for (--i; i>=0; --i) {
177 					if ((r->data)[i]) {
178 						free((r->data)[i]);
179 					}
180 				}
181 				free(r->data);
182 				free(r);
183 				return NULL;
184 			}
185 			strcpy((r->data)[i], values[i-1]);
186 		} else {
187 			(r->data)[i] = NULL;
188 		}
189 	}
190 
191 	debug(3, "Leaving replay_create_exec_prepared%s\n", "");
192 	return r;
193 }
194 
replay_create_cancel(const struct timeval * time,uint64_t session_id)195 replay_item *replay_create_cancel(const struct timeval *time, uint64_t session_id) {
196 	replay_item *r;
197 
198 	debug(3, "Entering replay_create_cancel%s\n", "");
199 
200 	r = replay_create(time, session_id, pg_cancel, 0);
201 	if (NULL == r) {
202 		return NULL;
203 	}
204 	debug(3, "Leaving replay_create_cancel%s\n", "");
205 	return r;
206 }
207 
replay_free(replay_item * r)208 void replay_free(replay_item *r) {
209 	int i;
210 
211 	debug(3, "Entering replay_free%s\n", "");
212 
213 	assert((pg_connect == r->type) || (pg_disconnect == r->type) || (pg_execute == r->type) || (pg_prepare == r->type) || (pg_exec_prepared == r->type) || (pg_cancel == r->type));
214 
215 	for (i=0; i<r->count; ++i) {
216 		if ((r->data)[i]) {
217 			free((r->data)[i]);
218 		}
219 	}
220 	if (r->count) {
221 		free(r->data);
222 	}
223 	free(r);
224 
225 	debug(3, "Leaving replay_free%s\n", "");
226 }
227 
replay_get_type(const replay_item * r)228 replay_type replay_get_type(const replay_item *r) {
229 	return r->type;
230 }
231 
replay_get_session_id(const replay_item * r)232 uint64_t replay_get_session_id(const replay_item *r) {
233 	return r->session_id;
234 }
235 
replay_get_time(const replay_item * r)236 const struct timeval * replay_get_time(const replay_item *r) {
237 	return &(r->time);
238 }
239 
replay_get_statement(const replay_item * r)240 const char * replay_get_statement(const replay_item *r) {
241 	assert((pg_execute == r->type) || (pg_prepare == r->type));
242 
243 	return (r->data)[0];
244 }
245 
replay_get_name(const replay_item * r)246 const char * replay_get_name(const replay_item *r) {
247 	assert((pg_prepare == r->type) || (pg_exec_prepared == r->type));
248 
249 	return (pg_prepare == r->type) ? (r->data)[1] : (r->data)[0];
250 }
251 
replay_get_user(const replay_item * r)252 const char * replay_get_user(const replay_item *r) {
253 	assert(pg_connect == r->type);
254 
255 	return (r->data)[0];
256 }
257 
replay_get_database(const replay_item * r)258 const char * replay_get_database(const replay_item *r) {
259 	assert(pg_connect == r->type);
260 
261 	return (r->data)[1];
262 }
263 
replay_get_valuecount(const replay_item * r)264 int replay_get_valuecount(const replay_item *r) {
265 	assert(pg_exec_prepared == r->type);
266 
267 	return r->count - 1;
268 }
269 
replay_get_values(const replay_item * r)270 const char * const * replay_get_values(const replay_item *r) {
271 	assert(pg_exec_prepared == r->type);
272 
273 	return (const char * const *)((r->data) + 1);
274 }
275 
276 /* maximal part of a value for display */
277 #define SAMPLE_SIZE 100
278 
replay_print_debug(const replay_item * r)279 void replay_print_debug(const replay_item *r) {
280 	replay_type type;
281 	int i;
282 	char valuepart[SAMPLE_SIZE+4], *p;
283 
284 	valuepart[SAMPLE_SIZE] = '.';
285 	valuepart[SAMPLE_SIZE+1] = '.';
286 	valuepart[SAMPLE_SIZE+2] = '.';
287 	valuepart[SAMPLE_SIZE+3] = '\0';
288 
289 	debug(1, "---------------------------%s\n", "");
290 	debug(1, "Item: time       = %lu.%06lu\n", (unsigned long)r->time.tv_sec, (unsigned long)r->time.tv_usec);
291 	debug(1, "      session id = 0x" UINT64_FORMAT "\n", r->session_id);
292 	type = r->type;
293 	debug(1, "      type       = %s\n",
294 		(pg_connect == type) ? "connect" :
295 			((pg_disconnect == type) ? "disconnect" :
296 				((pg_execute == type) ? "execute" :
297 					((pg_prepare == type) ? "prepare" :
298 						((pg_exec_prepared == type) ? "exec_prepared" :
299 							((pg_cancel == type) ? "cancel" : "unknown")
300 						)
301 					)
302 				)
303 			)
304 	);
305 	switch (type) {
306 		case pg_connect:
307 			debug(1, "      user       = %s\n", replay_get_user(r));
308 			debug(1, "      database   = %s\n", replay_get_database(r));
309 		case pg_disconnect:
310 		case pg_cancel:
311 			break;
312 		case pg_prepare:
313 			debug(1, "      name       = %s\n", replay_get_name(r));
314 		case pg_execute:
315 			debug(1, "      statement  = %s\n", replay_get_statement(r));
316 			break;
317 		case pg_exec_prepared:
318 			debug(1, "      name       = %s\n", replay_get_name(r));
319 			for (i=0; i<replay_get_valuecount(r); ++i) {
320 				/* print only the first SAMPLE_SIZE bytes of the argument */
321 				if (replay_get_values(r)[i]) {
322 					strncpy(valuepart, replay_get_values(r)[i], SAMPLE_SIZE);
323 					p = valuepart;
324 				} else {
325 					p = NULL;
326 				}
327 				debug(1, "      $%d         = %s\n", i + 1, (NULL == p) ? "(null)" : p);
328 			}
329 			break;
330 	}
331 	debug(1, "---------------------------%s\n", "");
332 }
333