Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * libpq_pipeline.c
4 : : * Verify libpq pipeline execution functionality
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/test/modules/libpq_pipeline/libpq_pipeline.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres_fe.h"
17 : :
18 : : #include <sys/select.h>
19 : : #include <sys/time.h>
20 : :
21 : : #include "catalog/pg_type_d.h"
22 : : #include "libpq-fe.h"
23 : : #include "pg_getopt.h"
24 : :
25 : :
26 : : static void exit_nicely(PGconn *conn);
27 : : pg_noreturn static void pg_fatal_impl(int line, const char *fmt, ...)
28 : : pg_attribute_printf(2, 3);
29 : : static bool process_result(PGconn *conn, PGresult *res, int results,
30 : : int numsent);
31 : :
32 : : static const char *const progname = "libpq_pipeline";
33 : :
34 : : /* Options and defaults */
35 : : static char *tracefile = NULL; /* path to PQtrace() file */
36 : :
37 : :
38 : : #ifdef DEBUG_OUTPUT
39 : : #define pg_debug(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
40 : : #else
41 : : #define pg_debug(...)
42 : : #endif
43 : :
44 : : static const char *const drop_table_sql =
45 : : "DROP TABLE IF EXISTS pq_pipeline_demo";
46 : : static const char *const create_table_sql =
47 : : "CREATE UNLOGGED TABLE pq_pipeline_demo(id serial primary key, itemno integer,"
48 : : "int8filler int8);";
49 : : static const char *const insert_sql =
50 : : "INSERT INTO pq_pipeline_demo(itemno) VALUES ($1)";
51 : : static const char *const insert_sql2 =
52 : : "INSERT INTO pq_pipeline_demo(itemno,int8filler) VALUES ($1, $2)";
53 : :
54 : : /* max char length of an int32/64, plus sign and null terminator */
55 : : #define MAXINTLEN 12
56 : : #define MAXINT8LEN 20
57 : :
58 : : static void
2037 alvherre@alvh.no-ip. 59 :UBC 0 : exit_nicely(PGconn *conn)
60 : : {
61 : 0 : PQfinish(conn);
62 : 0 : exit(1);
63 : : }
64 : :
65 : : /*
66 : : * The following few functions are wrapped in macros to make the reported line
67 : : * number in an error match the line number of the invocation.
68 : : */
69 : :
70 : : /*
71 : : * Print an error to stderr and terminate the program.
72 : : */
73 : : #define pg_fatal(...) pg_fatal_impl(__LINE__, __VA_ARGS__)
74 : : pg_noreturn static void
152 tgl@sss.pgh.pa.us 75 : 0 : pg_fatal_impl(int line, const char *fmt, ...)
76 : : {
77 : : va_list args;
78 : :
2037 alvherre@alvh.no-ip. 79 : 0 : fflush(stdout);
80 : :
81 : 0 : fprintf(stderr, "\n%s:%d: ", progname, line);
82 : 0 : va_start(args, fmt);
83 : 0 : vfprintf(stderr, fmt, args);
84 : 0 : va_end(args);
85 [ # # ]: 0 : Assert(fmt[strlen(fmt) - 1] != '\n');
86 : 0 : fprintf(stderr, "\n");
87 : 0 : exit(1);
88 : : }
89 : :
90 : : /*
91 : : * Check that libpq next returns a PGresult with the specified status,
92 : : * returning the PGresult so that caller can perform additional checks.
93 : : */
94 : : #define confirm_result_status(conn, status) confirm_result_status_impl(__LINE__, conn, status)
95 : : static PGresult *
404 tgl@sss.pgh.pa.us 96 :CBC 56 : confirm_result_status_impl(int line, PGconn *conn, ExecStatusType status)
97 : : {
98 : : PGresult *res;
99 : :
945 alvherre@alvh.no-ip. 100 : 56 : res = PQgetResult(conn);
101 [ - + ]: 56 : if (res == NULL)
404 tgl@sss.pgh.pa.us 102 :UBC 0 : pg_fatal_impl(line, "PQgetResult returned null unexpectedly: %s",
103 : : PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 104 [ - + ]:CBC 56 : if (PQresultStatus(res) != status)
404 tgl@sss.pgh.pa.us 105 :UBC 0 : pg_fatal_impl(line, "PQgetResult returned status %s, expected %s: %s",
106 : : PQresStatus(PQresultStatus(res)),
107 : : PQresStatus(status),
108 : : PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 109 :CBC 56 : return res;
110 : : }
111 : :
112 : : /*
113 : : * Check that libpq next returns a PGresult with the specified status,
114 : : * then free the PGresult.
115 : : */
116 : : #define consume_result_status(conn, status) consume_result_status_impl(__LINE__, conn, status)
117 : : static void
118 : 39 : consume_result_status_impl(int line, PGconn *conn, ExecStatusType status)
119 : : {
120 : : PGresult *res;
121 : :
122 : 39 : res = confirm_result_status_impl(line, conn, status);
123 : 39 : PQclear(res);
124 : 39 : }
125 : :
126 : : /*
127 : : * Check that libpq next returns a null PGresult.
128 : : */
129 : : #define consume_null_result(conn) consume_null_result_impl(__LINE__, conn)
130 : : static void
131 : 631 : consume_null_result_impl(int line, PGconn *conn)
132 : : {
133 : : PGresult *res;
134 : :
135 : 631 : res = PQgetResult(conn);
136 [ - + ]: 631 : if (res != NULL)
404 tgl@sss.pgh.pa.us 137 :UBC 0 : pg_fatal_impl(line, "expected NULL PGresult, got %s: %s",
138 : : PQresStatus(PQresultStatus(res)),
139 : : PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 140 :CBC 631 : }
141 : :
142 : : /*
143 : : * Check that the query on the given connection got canceled.
144 : : */
145 : : #define consume_query_cancel(conn) consume_query_cancel_impl(__LINE__, conn)
146 : : static void
147 : 12 : consume_query_cancel_impl(int line, PGconn *conn)
148 : : {
149 : : PGresult *res;
150 : :
151 : 12 : res = confirm_result_status_impl(line, conn, PGRES_FATAL_ERROR);
945 alvherre@alvh.no-ip. 152 [ - + ]: 12 : if (strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), "57014") != 0)
945 alvherre@alvh.no-ip. 153 :UBC 0 : pg_fatal_impl(line, "query failed with a different error than cancellation: %s",
154 : : PQerrorMessage(conn));
945 alvherre@alvh.no-ip. 155 :CBC 12 : PQclear(res);
156 : :
157 [ - + ]: 12 : while (PQisBusy(conn))
945 alvherre@alvh.no-ip. 158 :UBC 0 : PQconsumeInput(conn);
945 alvherre@alvh.no-ip. 159 :CBC 12 : }
160 : :
161 : : /*
162 : : * Using monitorConn, query pg_stat_activity to see that the connection with
163 : : * the given PID is either in the given state, or waiting on the given event
164 : : * (only one of them can be given).
165 : : */
166 : : static void
938 167 : 24 : wait_for_connection_state(int line, PGconn *monitorConn, int procpid,
168 : : char *state, char *event)
169 : : {
943 170 : 24 : const Oid paramTypes[] = {INT4OID, TEXTOID};
171 : : const char *paramValues[2];
172 : 24 : char *pidstr = psprintf("%d", procpid);
173 : :
938 174 [ - + ]: 24 : Assert((state == NULL) ^ (event == NULL));
175 : :
943 176 : 24 : paramValues[0] = pidstr;
938 177 [ + + ]: 24 : paramValues[1] = state ? state : event;
178 : :
179 : : while (true)
945 alvherre@alvh.no-ip. 180 :UBC 0 : {
181 : : PGresult *res;
182 : : char *value;
183 : :
938 alvherre@alvh.no-ip. 184 [ + + ]:CBC 24 : if (state != NULL)
185 : 12 : res = PQexecParams(monitorConn,
186 : : "SELECT count(*) FROM pg_stat_activity WHERE "
187 : : "pid = $1 AND state = $2",
188 : : 2, paramTypes, paramValues, NULL, NULL, 0);
189 : : else
190 : 12 : res = PQexecParams(monitorConn,
191 : : "SELECT count(*) FROM pg_stat_activity WHERE "
192 : : "pid = $1 AND wait_event = $2",
193 : : 2, paramTypes, paramValues, NULL, NULL, 0);
194 : :
945 195 [ - + ]: 24 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
943 alvherre@alvh.no-ip. 196 :UBC 0 : pg_fatal_impl(line, "could not query pg_stat_activity: %s", PQerrorMessage(monitorConn));
945 alvherre@alvh.no-ip. 197 [ - + ]:CBC 24 : if (PQntuples(res) != 1)
943 alvherre@alvh.no-ip. 198 :UBC 0 : pg_fatal_impl(line, "unexpected number of rows received: %d", PQntuples(res));
945 alvherre@alvh.no-ip. 199 [ - + ]:CBC 24 : if (PQnfields(res) != 1)
943 alvherre@alvh.no-ip. 200 :UBC 0 : pg_fatal_impl(line, "unexpected number of columns received: %d", PQnfields(res));
945 alvherre@alvh.no-ip. 201 :CBC 24 : value = PQgetvalue(res, 0, 0);
938 202 [ + - ]: 24 : if (strcmp(value, "0") != 0)
203 : : {
945 204 : 24 : PQclear(res);
205 : 24 : break;
206 : : }
945 alvherre@alvh.no-ip. 207 :UBC 0 : PQclear(res);
208 : :
209 : : /* wait 10ms before polling again */
210 : 0 : pg_usleep(10000);
211 : : }
212 : :
943 alvherre@alvh.no-ip. 213 :CBC 24 : pfree(pidstr);
214 : 24 : }
215 : :
216 : : #define send_cancellable_query(conn, monitorConn) \
217 : : send_cancellable_query_impl(__LINE__, conn, monitorConn)
218 : : static void
219 : 12 : send_cancellable_query_impl(int line, PGconn *conn, PGconn *monitorConn)
220 : : {
221 : : const char *env_wait;
222 : 12 : const Oid paramTypes[1] = {INT4OID};
223 : :
224 : : /*
225 : : * Wait for the connection to be idle, so that our check for an active
226 : : * connection below is reliable, instead of possibly seeing an outdated
227 : : * state.
228 : : */
938 229 : 12 : wait_for_connection_state(line, monitorConn, PQbackendPID(conn), "idle", NULL);
230 : :
943 231 : 12 : env_wait = getenv("PG_TEST_TIMEOUT_DEFAULT");
232 [ + - ]: 12 : if (env_wait == NULL)
233 : 12 : env_wait = "180";
234 : :
235 [ - + ]: 12 : if (PQsendQueryParams(conn, "SELECT pg_sleep($1)", 1, paramTypes,
236 : : &env_wait, NULL, NULL, 0) != 1)
943 alvherre@alvh.no-ip. 237 :UBC 0 : pg_fatal_impl(line, "failed to send query: %s", PQerrorMessage(conn));
238 : :
239 : : /*
240 : : * Wait for the sleep to be active, because if the query is not running
241 : : * yet, the cancel request that we send won't have any effect.
242 : : */
938 alvherre@alvh.no-ip. 243 :CBC 12 : wait_for_connection_state(line, monitorConn, PQbackendPID(conn), NULL, "PgSleep");
945 244 : 12 : }
245 : :
246 : : /*
247 : : * Create a new connection with the same conninfo as the given one.
248 : : */
249 : : static PGconn *
250 : 2 : copy_connection(PGconn *conn)
251 : : {
252 : : PGconn *copyConn;
253 : 2 : PQconninfoOption *opts = PQconninfo(conn);
254 : : const char **keywords;
255 : : const char **vals;
558 heikki.linnakangas@i 256 : 2 : int nopts = 0;
257 : : int i;
258 : :
945 alvherre@alvh.no-ip. 259 [ + + ]: 106 : for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
260 : 104 : nopts++;
558 heikki.linnakangas@i 261 : 2 : nopts++; /* for the NULL terminator */
262 : :
227 michael@paquier.xyz 263 : 2 : keywords = pg_malloc_array(const char *, nopts);
264 : 2 : vals = pg_malloc_array(const char *, nopts);
265 : :
558 heikki.linnakangas@i 266 : 2 : i = 0;
945 alvherre@alvh.no-ip. 267 [ + + ]: 106 : for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
268 : : {
269 [ + + ]: 104 : if (opt->val)
270 : : {
271 : 40 : keywords[i] = opt->keyword;
272 : 40 : vals[i] = opt->val;
273 : 40 : i++;
274 : : }
275 : : }
276 : 2 : keywords[i] = vals[i] = NULL;
277 : :
278 : 2 : copyConn = PQconnectdbParams(keywords, vals, false);
279 : :
280 [ - + ]: 2 : if (PQstatus(copyConn) != CONNECTION_OK)
945 alvherre@alvh.no-ip. 281 :UBC 0 : pg_fatal("Connection to database failed: %s",
282 : : PQerrorMessage(copyConn));
283 : :
103 peter@eisentraut.org 284 :GNC 2 : pg_free(keywords);
285 : 2 : pg_free(vals);
404 tgl@sss.pgh.pa.us 286 :CBC 2 : PQconninfoFree(opts);
287 : :
945 alvherre@alvh.no-ip. 288 : 2 : return copyConn;
289 : : }
290 : :
291 : : /*
292 : : * Test query cancellation routines
293 : : */
294 : : static void
295 : 2 : test_cancel(PGconn *conn)
296 : : {
297 : : PGcancel *cancel;
298 : : PGcancelConn *cancelConn;
299 : : PGconn *monitorConn;
300 : : char errorbuf[256];
301 : :
302 : 2 : fprintf(stderr, "test cancellations... ");
303 : :
304 [ - + ]: 2 : if (PQsetnonblocking(conn, 1) != 0)
945 alvherre@alvh.no-ip. 305 :UBC 0 : pg_fatal("failed to set nonblocking mode: %s", PQerrorMessage(conn));
306 : :
307 : : /*
308 : : * Make a separate connection to the database to monitor the query on the
309 : : * main connection.
310 : : */
945 alvherre@alvh.no-ip. 311 :CBC 2 : monitorConn = copy_connection(conn);
312 [ - + ]: 2 : Assert(PQstatus(monitorConn) == CONNECTION_OK);
313 : :
314 : : /* test PQcancel */
315 : 2 : send_cancellable_query(conn, monitorConn);
316 : 2 : cancel = PQgetCancel(conn);
317 [ - + ]: 2 : if (!PQcancel(cancel, errorbuf, sizeof(errorbuf)))
945 alvherre@alvh.no-ip. 318 :UBC 0 : pg_fatal("failed to run PQcancel: %s", errorbuf);
404 tgl@sss.pgh.pa.us 319 :CBC 2 : consume_query_cancel(conn);
320 : :
321 : : /* PGcancel object can be reused for the next query */
945 alvherre@alvh.no-ip. 322 : 2 : send_cancellable_query(conn, monitorConn);
323 [ - + ]: 2 : if (!PQcancel(cancel, errorbuf, sizeof(errorbuf)))
945 alvherre@alvh.no-ip. 324 :UBC 0 : pg_fatal("failed to run PQcancel: %s", errorbuf);
404 tgl@sss.pgh.pa.us 325 :CBC 2 : consume_query_cancel(conn);
326 : :
945 alvherre@alvh.no-ip. 327 : 2 : PQfreeCancel(cancel);
328 : :
329 : : /* test PQrequestCancel */
330 : 2 : send_cancellable_query(conn, monitorConn);
331 [ - + ]: 2 : if (!PQrequestCancel(conn))
945 alvherre@alvh.no-ip. 332 :UBC 0 : pg_fatal("failed to run PQrequestCancel: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 333 :CBC 2 : consume_query_cancel(conn);
334 : :
335 : : /* test PQcancelBlocking */
944 alvherre@alvh.no-ip. 336 : 2 : send_cancellable_query(conn, monitorConn);
337 : 2 : cancelConn = PQcancelCreate(conn);
338 [ - + ]: 2 : if (!PQcancelBlocking(cancelConn))
944 alvherre@alvh.no-ip. 339 :UBC 0 : pg_fatal("failed to run PQcancelBlocking: %s", PQcancelErrorMessage(cancelConn));
404 tgl@sss.pgh.pa.us 340 :CBC 2 : consume_query_cancel(conn);
944 alvherre@alvh.no-ip. 341 : 2 : PQcancelFinish(cancelConn);
342 : :
343 : : /* test PQcancelCreate and then polling with PQcancelPoll */
344 : 2 : send_cancellable_query(conn, monitorConn);
345 : 2 : cancelConn = PQcancelCreate(conn);
346 [ - + ]: 2 : if (!PQcancelStart(cancelConn))
944 alvherre@alvh.no-ip. 347 :UBC 0 : pg_fatal("bad cancel connection: %s", PQcancelErrorMessage(cancelConn));
348 : : while (true)
944 alvherre@alvh.no-ip. 349 :CBC 2 : {
350 : : struct timeval tv;
351 : : fd_set input_mask;
352 : : fd_set output_mask;
353 : 4 : PostgresPollingStatusType pollres = PQcancelPoll(cancelConn);
354 : 4 : int sock = PQcancelSocket(cancelConn);
355 : :
356 [ + + ]: 4 : if (pollres == PGRES_POLLING_OK)
357 : 2 : break;
358 : :
359 [ + + ]: 34 : FD_ZERO(&input_mask);
360 [ + + ]: 34 : FD_ZERO(&output_mask);
361 [ + - - ]: 2 : switch (pollres)
362 : : {
363 : 2 : case PGRES_POLLING_READING:
364 : : pg_debug("polling for reads\n");
365 : 2 : FD_SET(sock, &input_mask);
366 : 2 : break;
944 alvherre@alvh.no-ip. 367 :UBC 0 : case PGRES_POLLING_WRITING:
368 : : pg_debug("polling for writes\n");
369 : 0 : FD_SET(sock, &output_mask);
370 : 0 : break;
371 : 0 : default:
372 : 0 : pg_fatal("bad cancel connection: %s", PQcancelErrorMessage(cancelConn));
373 : : }
374 : :
944 alvherre@alvh.no-ip. 375 [ - + ]:CBC 2 : if (sock < 0)
944 alvherre@alvh.no-ip. 376 :UBC 0 : pg_fatal("sock did not exist: %s", PQcancelErrorMessage(cancelConn));
377 : :
944 alvherre@alvh.no-ip. 378 :CBC 2 : tv.tv_sec = 3;
379 : 2 : tv.tv_usec = 0;
380 : :
381 : : while (true)
382 : : {
383 [ - + ]: 2 : if (select(sock + 1, &input_mask, &output_mask, NULL, &tv) < 0)
384 : : {
944 alvherre@alvh.no-ip. 385 [ # # ]:UBC 0 : if (errno == EINTR)
386 : 0 : continue;
387 : 0 : pg_fatal("select() failed: %m");
388 : : }
944 alvherre@alvh.no-ip. 389 :CBC 2 : break;
390 : : }
391 : : }
392 [ - + ]: 2 : if (PQcancelStatus(cancelConn) != CONNECTION_OK)
944 alvherre@alvh.no-ip. 393 :UBC 0 : pg_fatal("unexpected cancel connection status: %s", PQcancelErrorMessage(cancelConn));
404 tgl@sss.pgh.pa.us 394 :CBC 2 : consume_query_cancel(conn);
395 : :
396 : : /*
397 : : * test PQcancelReset works on the cancel connection and it can be reused
398 : : * afterwards
399 : : */
944 alvherre@alvh.no-ip. 400 : 2 : PQcancelReset(cancelConn);
401 : :
402 : 2 : send_cancellable_query(conn, monitorConn);
403 [ - + ]: 2 : if (!PQcancelStart(cancelConn))
944 alvherre@alvh.no-ip. 404 :UBC 0 : pg_fatal("bad cancel connection: %s", PQcancelErrorMessage(cancelConn));
405 : : while (true)
944 alvherre@alvh.no-ip. 406 :CBC 2 : {
407 : : struct timeval tv;
408 : : fd_set input_mask;
409 : : fd_set output_mask;
410 : 4 : PostgresPollingStatusType pollres = PQcancelPoll(cancelConn);
411 : 4 : int sock = PQcancelSocket(cancelConn);
412 : :
413 [ + + ]: 4 : if (pollres == PGRES_POLLING_OK)
414 : 2 : break;
415 : :
416 [ + + ]: 34 : FD_ZERO(&input_mask);
417 [ + + ]: 34 : FD_ZERO(&output_mask);
418 [ + - - ]: 2 : switch (pollres)
419 : : {
420 : 2 : case PGRES_POLLING_READING:
421 : : pg_debug("polling for reads\n");
422 : 2 : FD_SET(sock, &input_mask);
423 : 2 : break;
944 alvherre@alvh.no-ip. 424 :UBC 0 : case PGRES_POLLING_WRITING:
425 : : pg_debug("polling for writes\n");
426 : 0 : FD_SET(sock, &output_mask);
427 : 0 : break;
428 : 0 : default:
429 : 0 : pg_fatal("bad cancel connection: %s", PQcancelErrorMessage(cancelConn));
430 : : }
431 : :
944 alvherre@alvh.no-ip. 432 [ - + ]:CBC 2 : if (sock < 0)
944 alvherre@alvh.no-ip. 433 :UBC 0 : pg_fatal("sock did not exist: %s", PQcancelErrorMessage(cancelConn));
434 : :
944 alvherre@alvh.no-ip. 435 :CBC 2 : tv.tv_sec = 3;
436 : 2 : tv.tv_usec = 0;
437 : :
438 : : while (true)
439 : : {
440 [ - + ]: 2 : if (select(sock + 1, &input_mask, &output_mask, NULL, &tv) < 0)
441 : : {
944 alvherre@alvh.no-ip. 442 [ # # ]:UBC 0 : if (errno == EINTR)
443 : 0 : continue;
444 : 0 : pg_fatal("select() failed: %m");
445 : : }
944 alvherre@alvh.no-ip. 446 :CBC 2 : break;
447 : : }
448 : : }
449 [ - + ]: 2 : if (PQcancelStatus(cancelConn) != CONNECTION_OK)
944 alvherre@alvh.no-ip. 450 :UBC 0 : pg_fatal("unexpected cancel connection status: %s", PQcancelErrorMessage(cancelConn));
404 tgl@sss.pgh.pa.us 451 :CBC 2 : consume_query_cancel(conn);
452 : :
944 alvherre@alvh.no-ip. 453 : 2 : PQcancelFinish(cancelConn);
404 tgl@sss.pgh.pa.us 454 : 2 : PQfinish(monitorConn);
455 : :
945 alvherre@alvh.no-ip. 456 : 2 : fprintf(stderr, "ok\n");
457 : 2 : }
458 : :
459 : : /*
460 : : * Test Describe of a prepared FETCH statement after the cursor it
461 : : * references has been closed.
462 : : */
463 : : static void
10 michael@paquier.xyz 464 : 1 : test_describe_fetch(PGconn *conn)
465 : : {
466 : : PGresult *res;
467 : :
468 : 1 : fprintf(stderr, "test cursor describe and fetch... ");
469 : :
470 : 1 : res = PQexec(conn, "BEGIN");
471 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
10 michael@paquier.xyz 472 :UBC 0 : pg_fatal("BEGIN failed: %s", PQerrorMessage(conn));
10 michael@paquier.xyz 473 :CBC 1 : PQclear(res);
474 : :
475 : 1 : res = PQexec(conn, "DECLARE fetch_cursor CURSOR FOR SELECT 1");
476 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
10 michael@paquier.xyz 477 :UBC 0 : pg_fatal("DECLARE CURSOR failed: %s", PQerrorMessage(conn));
10 michael@paquier.xyz 478 :CBC 1 : PQclear(res);
479 : :
480 : : /* prepare while the cursor exists, so that it caches a result desc */
481 : 1 : res = PQprepare(conn, "fetch_one", "FETCH 1 FROM fetch_cursor", 0, NULL);
482 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
10 michael@paquier.xyz 483 :UBC 0 : pg_fatal("PQprepare failed: %s", PQerrorMessage(conn));
10 michael@paquier.xyz 484 :CBC 1 : PQclear(res);
485 : :
486 : 1 : res = PQexec(conn, "CLOSE fetch_cursor");
487 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
10 michael@paquier.xyz 488 :UBC 0 : pg_fatal("CLOSE failed: %s", PQerrorMessage(conn));
10 michael@paquier.xyz 489 :CBC 1 : PQclear(res);
490 : :
491 : : /* describe fails after the cursor has been closed */
492 : 1 : res = PQdescribePrepared(conn, "fetch_one");
493 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_FATAL_ERROR)
10 michael@paquier.xyz 494 :UBC 0 : pg_fatal("expected FATAL_ERROR, got %s", PQresStatus(PQresultStatus(res)));
10 michael@paquier.xyz 495 :CBC 1 : PQclear(res);
496 : :
497 : 1 : res = PQexec(conn, "ROLLBACK");
498 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
10 michael@paquier.xyz 499 :UBC 0 : pg_fatal("ROLLBACK failed: %s", PQerrorMessage(conn));
10 michael@paquier.xyz 500 :CBC 1 : PQclear(res);
501 : :
502 : 1 : fprintf(stderr, "ok\n");
503 : 1 : }
504 : :
505 : : static void
2037 alvherre@alvh.no-ip. 506 : 1 : test_disallowed_in_pipeline(PGconn *conn)
507 : : {
508 : 1 : PGresult *res = NULL;
509 : :
510 : 1 : fprintf(stderr, "test error cases... ");
511 : :
512 [ - + ]: 1 : if (PQisnonblocking(conn))
2037 alvherre@alvh.no-ip. 513 :UBC 0 : pg_fatal("Expected blocking connection mode");
514 : :
2037 alvherre@alvh.no-ip. 515 [ - + ]:CBC 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 516 :UBC 0 : pg_fatal("Unable to enter pipeline mode");
517 : :
2037 alvherre@alvh.no-ip. 518 [ - + ]:CBC 1 : if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 519 :UBC 0 : pg_fatal("Pipeline mode not activated properly");
520 : :
521 : : /* PQexec should fail in pipeline mode */
2037 alvherre@alvh.no-ip. 522 :CBC 1 : res = PQexec(conn, "SELECT 1");
523 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_FATAL_ERROR)
2037 alvherre@alvh.no-ip. 524 :UBC 0 : pg_fatal("PQexec should fail in pipeline mode but succeeded");
1480 alvherre@alvh.no-ip. 525 [ - + ]:CBC 1 : if (strcmp(PQerrorMessage(conn),
526 : : "synchronous command execution functions are not allowed in pipeline mode\n") != 0)
1480 alvherre@alvh.no-ip. 527 :UBC 0 : pg_fatal("did not get expected error message; got: \"%s\"",
528 : : PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 529 :CBC 1 : PQclear(res);
530 : :
531 : : /* PQsendQuery should fail in pipeline mode */
1480 alvherre@alvh.no-ip. 532 [ - + ]: 1 : if (PQsendQuery(conn, "SELECT 1") != 0)
1480 alvherre@alvh.no-ip. 533 :UBC 0 : pg_fatal("PQsendQuery should fail in pipeline mode but succeeded");
1480 alvherre@alvh.no-ip. 534 [ - + ]:CBC 1 : if (strcmp(PQerrorMessage(conn),
535 : : "PQsendQuery not allowed in pipeline mode\n") != 0)
1480 alvherre@alvh.no-ip. 536 :UBC 0 : pg_fatal("did not get expected error message; got: \"%s\"",
537 : : PQerrorMessage(conn));
538 : :
539 : : /* Entering pipeline mode when already in pipeline mode is OK */
2037 alvherre@alvh.no-ip. 540 [ - + ]:CBC 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 541 :UBC 0 : pg_fatal("re-entering pipeline mode should be a no-op but failed");
542 : :
2037 alvherre@alvh.no-ip. 543 [ - + ]:CBC 1 : if (PQisBusy(conn) != 0)
2037 alvherre@alvh.no-ip. 544 :UBC 0 : pg_fatal("PQisBusy should return 0 when idle in pipeline mode, returned 1");
545 : :
546 : : /* ok, back to normal command mode */
2037 alvherre@alvh.no-ip. 547 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 548 :UBC 0 : pg_fatal("couldn't exit idle empty pipeline mode");
549 : :
2037 alvherre@alvh.no-ip. 550 [ - + ]:CBC 1 : if (PQpipelineStatus(conn) != PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 551 :UBC 0 : pg_fatal("Pipeline mode not terminated properly");
552 : :
553 : : /* exiting pipeline mode when not in pipeline mode should be a no-op */
2037 alvherre@alvh.no-ip. 554 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 555 :UBC 0 : pg_fatal("pipeline mode exit when not in pipeline mode should succeed but failed");
556 : :
557 : : /* can now PQexec again */
2037 alvherre@alvh.no-ip. 558 :CBC 1 : res = PQexec(conn, "SELECT 1");
559 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
2037 alvherre@alvh.no-ip. 560 :UBC 0 : pg_fatal("PQexec should succeed after exiting pipeline mode but failed with: %s",
561 : : PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 562 :CBC 1 : PQclear(res);
563 : :
2037 alvherre@alvh.no-ip. 564 : 1 : fprintf(stderr, "ok\n");
565 : 1 : }
566 : :
567 : : static void
568 : 1 : test_multi_pipelines(PGconn *conn)
569 : : {
570 : 1 : const char *dummy_params[1] = {"1"};
571 : 1 : Oid dummy_param_oids[1] = {INT4OID};
572 : :
573 : 1 : fprintf(stderr, "multi pipeline... ");
574 : :
575 : : /*
576 : : * Queue up a couple of small pipelines and process each without returning
577 : : * to command mode first.
578 : : */
579 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 580 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
581 : :
582 : : /* first pipeline */
2037 alvherre@alvh.no-ip. 583 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT $1", 1, dummy_param_oids,
584 : : dummy_params, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 585 :UBC 0 : pg_fatal("dispatching first SELECT failed: %s", PQerrorMessage(conn));
586 : :
2037 alvherre@alvh.no-ip. 587 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 588 :UBC 0 : pg_fatal("Pipeline sync failed: %s", PQerrorMessage(conn));
589 : :
590 : : /* second pipeline */
2037 alvherre@alvh.no-ip. 591 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT $1", 1, dummy_param_oids,
592 : : dummy_params, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 593 :UBC 0 : pg_fatal("dispatching second SELECT failed: %s", PQerrorMessage(conn));
594 : :
595 : : /* Skip flushing once. */
1000 michael@paquier.xyz 596 [ - + ]:CBC 1 : if (PQsendPipelineSync(conn) != 1)
1000 michael@paquier.xyz 597 :UBC 0 : pg_fatal("Pipeline sync failed: %s", PQerrorMessage(conn));
598 : :
599 : : /* third pipeline */
1000 michael@paquier.xyz 600 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT $1", 1, dummy_param_oids,
601 : : dummy_params, NULL, NULL, 0) != 1)
1000 michael@paquier.xyz 602 :UBC 0 : pg_fatal("dispatching third SELECT failed: %s", PQerrorMessage(conn));
603 : :
2037 alvherre@alvh.no-ip. 604 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 605 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
606 : :
607 : : /* OK, start processing the results */
608 : :
609 : : /* first pipeline */
404 tgl@sss.pgh.pa.us 610 :CBC 1 : consume_result_status(conn, PGRES_TUPLES_OK);
611 : :
612 : 1 : consume_null_result(conn);
613 : :
2037 alvherre@alvh.no-ip. 614 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 0)
2037 alvherre@alvh.no-ip. 615 :UBC 0 : pg_fatal("exiting pipeline mode after query but before sync succeeded incorrectly");
616 : :
404 tgl@sss.pgh.pa.us 617 :CBC 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
618 : :
619 : : /* second pipeline */
620 : 1 : consume_result_status(conn, PGRES_TUPLES_OK);
621 : :
622 : 1 : consume_null_result(conn);
623 : :
1000 michael@paquier.xyz 624 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 0)
1000 michael@paquier.xyz 625 :UBC 0 : pg_fatal("exiting pipeline mode after query but before sync succeeded incorrectly");
626 : :
404 tgl@sss.pgh.pa.us 627 :CBC 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
628 : :
629 : : /* third pipeline */
630 : 1 : consume_result_status(conn, PGRES_TUPLES_OK);
631 : :
632 : 1 : consume_null_result(conn);
633 : :
634 : 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
635 : :
636 : : /* We're still in pipeline mode ... */
2037 alvherre@alvh.no-ip. 637 [ - + ]: 1 : if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 638 :UBC 0 : pg_fatal("Fell out of pipeline mode somehow");
639 : :
640 : : /* until we end it, which we can safely do now */
2037 alvherre@alvh.no-ip. 641 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 642 :UBC 0 : pg_fatal("attempt to exit pipeline mode failed when it should've succeeded: %s",
643 : : PQerrorMessage(conn));
644 : :
2037 alvherre@alvh.no-ip. 645 [ - + ]:CBC 1 : if (PQpipelineStatus(conn) != PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 646 :UBC 0 : pg_fatal("exiting pipeline mode didn't seem to work");
647 : :
2037 alvherre@alvh.no-ip. 648 :CBC 1 : fprintf(stderr, "ok\n");
649 : 1 : }
650 : :
651 : : /*
652 : : * Test behavior when a pipeline dispatches a number of commands that are
653 : : * not flushed by a sync point.
654 : : */
655 : : static void
1931 656 : 1 : test_nosync(PGconn *conn)
657 : : {
658 : 1 : int numqueries = 10;
659 : 1 : int results = 0;
660 : 1 : int sock = PQsocket(conn);
661 : :
662 : 1 : fprintf(stderr, "nosync... ");
663 : :
664 [ - + ]: 1 : if (sock < 0)
1931 alvherre@alvh.no-ip. 665 :UBC 0 : pg_fatal("invalid socket");
666 : :
1931 alvherre@alvh.no-ip. 667 [ - + ]:CBC 1 : if (PQenterPipelineMode(conn) != 1)
1931 alvherre@alvh.no-ip. 668 :UBC 0 : pg_fatal("could not enter pipeline mode");
1931 alvherre@alvh.no-ip. 669 [ + + ]:CBC 11 : for (int i = 0; i < numqueries; i++)
670 : : {
671 : : fd_set input_mask;
672 : : struct timeval tv;
673 : :
674 [ - + ]: 10 : if (PQsendQueryParams(conn, "SELECT repeat('xyzxz', 12)",
675 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
1931 alvherre@alvh.no-ip. 676 :UBC 0 : pg_fatal("error sending select: %s", PQerrorMessage(conn));
1931 alvherre@alvh.no-ip. 677 :CBC 10 : PQflush(conn);
678 : :
679 : : /*
680 : : * If the server has written anything to us, read (some of) it now.
681 : : */
682 [ + + ]: 170 : FD_ZERO(&input_mask);
683 : 10 : FD_SET(sock, &input_mask);
684 : 10 : tv.tv_sec = 0;
685 : 10 : tv.tv_usec = 0;
686 [ - + ]: 10 : if (select(sock + 1, &input_mask, NULL, NULL, &tv) < 0)
687 : : {
944 michael@paquier.xyz 688 :UBC 0 : fprintf(stderr, "select() failed: %m\n");
1931 alvherre@alvh.no-ip. 689 : 0 : exit_nicely(conn);
690 : : }
1931 alvherre@alvh.no-ip. 691 [ - + - - ]:CBC 10 : if (FD_ISSET(sock, &input_mask) && PQconsumeInput(conn) != 1)
1931 alvherre@alvh.no-ip. 692 :UBC 0 : pg_fatal("failed to read from server: %s", PQerrorMessage(conn));
693 : : }
694 : :
695 : : /* tell server to flush its output buffer */
1931 alvherre@alvh.no-ip. 696 [ - + ]:CBC 1 : if (PQsendFlushRequest(conn) != 1)
1931 alvherre@alvh.no-ip. 697 :UBC 0 : pg_fatal("failed to send flush request");
1931 alvherre@alvh.no-ip. 698 :CBC 1 : PQflush(conn);
699 : :
700 : : /* Now read all results */
701 : : for (;;)
702 : : {
703 : : /* We expect exactly one TUPLES_OK result for each query we sent */
404 tgl@sss.pgh.pa.us 704 : 10 : consume_result_status(conn, PGRES_TUPLES_OK);
705 : :
706 : : /* and one NULL result should follow each */
707 : 10 : consume_null_result(conn);
708 : :
709 : 10 : results++;
710 : :
711 : : /* if we're done, we're done */
712 [ + + ]: 10 : if (results == numqueries)
713 : 1 : break;
714 : : }
715 : :
1931 alvherre@alvh.no-ip. 716 : 1 : fprintf(stderr, "ok\n");
717 : 1 : }
718 : :
719 : : /*
720 : : * When an operation in a pipeline fails the rest of the pipeline is flushed. We
721 : : * still have to get results for each pipeline item, but the item will just be
722 : : * a PGRES_PIPELINE_ABORTED code.
723 : : *
724 : : * This intentionally doesn't use a transaction to wrap the pipeline. You should
725 : : * usually use an xact, but in this case we want to observe the effects of each
726 : : * statement.
727 : : */
728 : : static void
2037 729 : 1 : test_pipeline_abort(PGconn *conn)
730 : : {
731 : 1 : PGresult *res = NULL;
732 : 1 : const char *dummy_params[1] = {"1"};
733 : 1 : Oid dummy_param_oids[1] = {INT4OID};
734 : : int i;
735 : : int gotrows;
736 : : bool goterror;
737 : :
738 : 1 : fprintf(stderr, "aborted pipeline... ");
739 : :
740 : 1 : res = PQexec(conn, drop_table_sql);
741 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2037 alvherre@alvh.no-ip. 742 :UBC 0 : pg_fatal("dispatching DROP TABLE failed: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 743 :CBC 1 : PQclear(res);
744 : :
2037 alvherre@alvh.no-ip. 745 : 1 : res = PQexec(conn, create_table_sql);
746 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2037 alvherre@alvh.no-ip. 747 :UBC 0 : pg_fatal("dispatching CREATE TABLE failed: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 748 :CBC 1 : PQclear(res);
749 : :
750 : : /*
751 : : * Queue up a couple of small pipelines and process each without returning
752 : : * to command mode first. Make sure the second operation in the first
753 : : * pipeline ERRORs.
754 : : */
2037 alvherre@alvh.no-ip. 755 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 756 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
757 : :
2037 alvherre@alvh.no-ip. 758 :CBC 1 : dummy_params[0] = "1";
759 [ - + ]: 1 : if (PQsendQueryParams(conn, insert_sql, 1, dummy_param_oids,
760 : : dummy_params, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 761 :UBC 0 : pg_fatal("dispatching first insert failed: %s", PQerrorMessage(conn));
762 : :
2037 alvherre@alvh.no-ip. 763 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT no_such_function($1)",
764 : : 1, dummy_param_oids, dummy_params,
765 : : NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 766 :UBC 0 : pg_fatal("dispatching error select failed: %s", PQerrorMessage(conn));
767 : :
2037 alvherre@alvh.no-ip. 768 :CBC 1 : dummy_params[0] = "2";
769 [ - + ]: 1 : if (PQsendQueryParams(conn, insert_sql, 1, dummy_param_oids,
770 : : dummy_params, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 771 :UBC 0 : pg_fatal("dispatching second insert failed: %s", PQerrorMessage(conn));
772 : :
2037 alvherre@alvh.no-ip. 773 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 774 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
775 : :
2037 alvherre@alvh.no-ip. 776 :CBC 1 : dummy_params[0] = "3";
777 [ - + ]: 1 : if (PQsendQueryParams(conn, insert_sql, 1, dummy_param_oids,
778 : : dummy_params, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 779 :UBC 0 : pg_fatal("dispatching second-pipeline insert failed: %s",
780 : : PQerrorMessage(conn));
781 : :
2037 alvherre@alvh.no-ip. 782 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 783 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
784 : :
785 : : /*
786 : : * OK, start processing the pipeline results.
787 : : *
788 : : * We should get a command-ok for the first query, then a fatal error and
789 : : * a pipeline aborted message for the second insert, a pipeline-end, then
790 : : * a command-ok and a pipeline-ok for the second pipeline operation.
791 : : */
404 tgl@sss.pgh.pa.us 792 :CBC 1 : consume_result_status(conn, PGRES_COMMAND_OK);
793 : :
794 : : /* NULL result to signal end-of-results for this command */
795 : 1 : consume_null_result(conn);
796 : :
797 : : /* Second query caused error, so we expect an error next */
798 : 1 : consume_result_status(conn, PGRES_FATAL_ERROR);
799 : :
800 : : /* NULL result to signal end-of-results for this command */
801 : 1 : consume_null_result(conn);
802 : :
803 : : /*
804 : : * pipeline should now be aborted.
805 : : *
806 : : * Note that we could still queue more queries at this point if we wanted;
807 : : * they'd get added to a new third pipeline since we've already sent a
808 : : * second. The aborted flag relates only to the pipeline being received.
809 : : */
2037 alvherre@alvh.no-ip. 810 [ - + ]: 1 : if (PQpipelineStatus(conn) != PQ_PIPELINE_ABORTED)
2037 alvherre@alvh.no-ip. 811 :UBC 0 : pg_fatal("pipeline should be flagged as aborted but isn't");
812 : :
813 : : /* third query in pipeline, the second insert */
404 tgl@sss.pgh.pa.us 814 :CBC 1 : consume_result_status(conn, PGRES_PIPELINE_ABORTED);
815 : :
816 : : /* NULL result to signal end-of-results for this command */
817 : 1 : consume_null_result(conn);
818 : :
2037 alvherre@alvh.no-ip. 819 [ - + ]: 1 : if (PQpipelineStatus(conn) != PQ_PIPELINE_ABORTED)
2037 alvherre@alvh.no-ip. 820 :UBC 0 : pg_fatal("pipeline should be flagged as aborted but isn't");
821 : :
822 : : /* Ensure we're still in pipeline */
2037 alvherre@alvh.no-ip. 823 [ - + ]:CBC 1 : if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 824 :UBC 0 : pg_fatal("Fell out of pipeline mode somehow");
825 : :
826 : : /*
827 : : * The end of a failed pipeline is a PGRES_PIPELINE_SYNC.
828 : : *
829 : : * (This is so clients know to start processing results normally again and
830 : : * can tell the difference between skipped commands and the sync.)
831 : : */
404 tgl@sss.pgh.pa.us 832 :CBC 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
833 : :
2037 alvherre@alvh.no-ip. 834 [ - + ]: 1 : if (PQpipelineStatus(conn) == PQ_PIPELINE_ABORTED)
2037 alvherre@alvh.no-ip. 835 :UBC 0 : pg_fatal("sync should've cleared the aborted flag but didn't");
836 : :
837 : : /* We're still in pipeline mode... */
2037 alvherre@alvh.no-ip. 838 [ - + ]:CBC 1 : if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 839 :UBC 0 : pg_fatal("Fell out of pipeline mode somehow");
840 : :
841 : : /* the insert from the second pipeline */
404 tgl@sss.pgh.pa.us 842 :CBC 1 : consume_result_status(conn, PGRES_COMMAND_OK);
843 : :
844 : : /* Read the NULL result at the end of the command */
845 : 1 : consume_null_result(conn);
846 : :
847 : : /* the second pipeline sync */
848 : 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
849 : :
850 : : /* Read the NULL result at the end of the command */
851 : 1 : consume_null_result(conn);
852 : :
853 : : /* Try to send two queries in one command */
1480 alvherre@alvh.no-ip. 854 [ - + ]: 1 : if (PQsendQueryParams(conn, "SELECT 1; SELECT 2", 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 855 :UBC 0 : pg_fatal("failed to send query: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 856 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 857 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 858 :CBC 1 : goterror = false;
859 [ + + ]: 2 : while ((res = PQgetResult(conn)) != NULL)
860 : : {
861 [ + - ]: 1 : switch (PQresultStatus(res))
862 : : {
863 : 1 : case PGRES_FATAL_ERROR:
864 [ - + ]: 1 : if (strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), "42601") != 0)
2037 alvherre@alvh.no-ip. 865 :UBC 0 : pg_fatal("expected error about multiple commands, got %s",
866 : : PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 867 :CBC 1 : printf("got expected %s", PQerrorMessage(conn));
868 : 1 : goterror = true;
869 : 1 : break;
2037 alvherre@alvh.no-ip. 870 :UBC 0 : default:
871 : 0 : pg_fatal("got unexpected status %s", PQresStatus(PQresultStatus(res)));
872 : : break;
873 : : }
404 tgl@sss.pgh.pa.us 874 :CBC 1 : PQclear(res);
875 : : }
2037 alvherre@alvh.no-ip. 876 [ - + ]: 1 : if (!goterror)
2037 alvherre@alvh.no-ip. 877 :UBC 0 : pg_fatal("did not get cannot-insert-multiple-commands error");
878 : :
879 : : /* the second pipeline sync */
404 tgl@sss.pgh.pa.us 880 :CBC 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
881 : :
2021 alvherre@alvh.no-ip. 882 : 1 : fprintf(stderr, "ok\n");
883 : :
884 : : /* Test single-row mode with an error partways */
1480 885 [ - + ]: 1 : if (PQsendQueryParams(conn, "SELECT 1.0/g FROM generate_series(3, -1, -1) g",
886 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 887 :UBC 0 : pg_fatal("failed to send query: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 888 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 889 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 890 :CBC 1 : PQsetSingleRowMode(conn);
891 : 1 : goterror = false;
2021 892 : 1 : gotrows = 0;
2037 893 [ + + ]: 5 : while ((res = PQgetResult(conn)) != NULL)
894 : : {
895 [ + + - ]: 4 : switch (PQresultStatus(res))
896 : : {
897 : 3 : case PGRES_SINGLE_TUPLE:
898 : 3 : printf("got row: %s\n", PQgetvalue(res, 0, 0));
2021 899 : 3 : gotrows++;
2037 900 : 3 : break;
901 : 1 : case PGRES_FATAL_ERROR:
902 [ - + ]: 1 : if (strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), "22012") != 0)
2037 alvherre@alvh.no-ip. 903 :UBC 0 : pg_fatal("expected division-by-zero, got: %s (%s)",
904 : : PQerrorMessage(conn),
905 : : PQresultErrorField(res, PG_DIAG_SQLSTATE));
2037 alvherre@alvh.no-ip. 906 :CBC 1 : printf("got expected division-by-zero\n");
907 : 1 : goterror = true;
908 : 1 : break;
2037 alvherre@alvh.no-ip. 909 :UBC 0 : default:
910 : 0 : pg_fatal("got unexpected result %s", PQresStatus(PQresultStatus(res)));
911 : : }
2037 alvherre@alvh.no-ip. 912 :CBC 4 : PQclear(res);
913 : : }
914 [ - + ]: 1 : if (!goterror)
2037 alvherre@alvh.no-ip. 915 :UBC 0 : pg_fatal("did not get division-by-zero error");
2021 alvherre@alvh.no-ip. 916 [ - + ]:CBC 1 : if (gotrows != 3)
2021 alvherre@alvh.no-ip. 917 :UBC 0 : pg_fatal("did not get three rows");
918 : :
919 : : /* the third pipeline sync */
404 tgl@sss.pgh.pa.us 920 :CBC 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
921 : :
922 : : /* We're still in pipeline mode... */
2037 alvherre@alvh.no-ip. 923 [ - + ]: 1 : if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 924 :UBC 0 : pg_fatal("Fell out of pipeline mode somehow");
925 : :
926 : : /* until we end it, which we can safely do now */
2037 alvherre@alvh.no-ip. 927 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 928 :UBC 0 : pg_fatal("attempt to exit pipeline mode failed when it should've succeeded: %s",
929 : : PQerrorMessage(conn));
930 : :
2037 alvherre@alvh.no-ip. 931 [ - + ]:CBC 1 : if (PQpipelineStatus(conn) != PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 932 :UBC 0 : pg_fatal("exiting pipeline mode didn't seem to work");
933 : :
934 : : /*-
935 : : * Since we fired the pipelines off without a surrounding xact, the results
936 : : * should be:
937 : : *
938 : : * - Implicit xact started by server around 1st pipeline
939 : : * - First insert applied
940 : : * - Second statement aborted xact
941 : : * - Third insert skipped
942 : : * - Sync rolled back first implicit xact
943 : : * - Implicit xact created by server around 2nd pipeline
944 : : * - insert applied from 2nd pipeline
945 : : * - Sync commits 2nd xact
946 : : *
947 : : * So we should only have the value 3 that we inserted.
948 : : */
2037 alvherre@alvh.no-ip. 949 :CBC 1 : res = PQexec(conn, "SELECT itemno FROM pq_pipeline_demo");
950 : :
951 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
2037 alvherre@alvh.no-ip. 952 :UBC 0 : pg_fatal("Expected tuples, got %s: %s",
953 : : PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 954 [ - + ]:CBC 1 : if (PQntuples(res) != 1)
2037 alvherre@alvh.no-ip. 955 :UBC 0 : pg_fatal("expected 1 result, got %d", PQntuples(res));
2037 alvherre@alvh.no-ip. 956 [ + + ]:CBC 2 : for (i = 0; i < PQntuples(res); i++)
957 : : {
958 : 1 : const char *val = PQgetvalue(res, i, 0);
959 : :
960 [ - + ]: 1 : if (strcmp(val, "3") != 0)
2037 alvherre@alvh.no-ip. 961 :UBC 0 : pg_fatal("expected only insert with value 3, got %s", val);
962 : : }
963 : :
2037 alvherre@alvh.no-ip. 964 :CBC 1 : PQclear(res);
965 : :
1560 966 : 1 : fprintf(stderr, "ok\n");
2037 967 : 1 : }
968 : :
969 : : /* State machine enum for test_pipelined_insert */
970 : : enum PipelineInsertStep
971 : : {
972 : : BI_BEGIN_TX,
973 : : BI_DROP_TABLE,
974 : : BI_CREATE_TABLE,
975 : : BI_PREPARE,
976 : : BI_INSERT_ROWS,
977 : : BI_COMMIT_TX,
978 : : BI_SYNC,
979 : : BI_DONE,
980 : : };
981 : :
982 : : static void
983 : 1 : test_pipelined_insert(PGconn *conn, int n_rows)
984 : : {
2021 985 : 1 : Oid insert_param_oids[2] = {INT4OID, INT8OID};
986 : : const char *insert_params[2];
987 : : char insert_param_0[MAXINTLEN];
988 : : char insert_param_1[MAXINT8LEN];
2037 989 : 1 : enum PipelineInsertStep send_step = BI_BEGIN_TX,
990 : 1 : recv_step = BI_BEGIN_TX;
991 : : int rows_to_send,
992 : : rows_to_receive;
993 : :
2021 994 : 1 : insert_params[0] = insert_param_0;
995 : 1 : insert_params[1] = insert_param_1;
996 : :
2037 997 : 1 : rows_to_send = rows_to_receive = n_rows;
998 : :
999 : : /*
1000 : : * Do a pipelined insert into a table created at the start of the pipeline
1001 : : */
1002 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1003 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
1004 : :
2037 alvherre@alvh.no-ip. 1005 [ + + ]:CBC 4 : while (send_step != BI_PREPARE)
1006 : : {
1007 : : const char *sql;
1008 : :
1009 [ + + + - ]: 3 : switch (send_step)
1010 : : {
1011 : 1 : case BI_BEGIN_TX:
1012 : 1 : sql = "BEGIN TRANSACTION";
1013 : 1 : send_step = BI_DROP_TABLE;
1014 : 1 : break;
1015 : :
1016 : 1 : case BI_DROP_TABLE:
1017 : 1 : sql = drop_table_sql;
1018 : 1 : send_step = BI_CREATE_TABLE;
1019 : 1 : break;
1020 : :
1021 : 1 : case BI_CREATE_TABLE:
1022 : 1 : sql = create_table_sql;
1023 : 1 : send_step = BI_PREPARE;
1024 : 1 : break;
1025 : :
2037 alvherre@alvh.no-ip. 1026 :UBC 0 : default:
1027 : 0 : pg_fatal("invalid state");
1028 : : sql = NULL; /* keep compiler quiet */
1029 : : }
1030 : :
1031 : : pg_debug("sending: %s\n", sql);
2037 alvherre@alvh.no-ip. 1032 [ - + ]:CBC 3 : if (PQsendQueryParams(conn, sql,
1033 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 1034 :UBC 0 : pg_fatal("dispatching %s failed: %s", sql, PQerrorMessage(conn));
1035 : : }
1036 : :
2037 alvherre@alvh.no-ip. 1037 [ - + ]:CBC 1 : Assert(send_step == BI_PREPARE);
1038 : : pg_debug("sending: %s\n", insert_sql2);
2021 1039 [ - + ]: 1 : if (PQsendPrepare(conn, "my_insert", insert_sql2, 2, insert_param_oids) != 1)
2037 alvherre@alvh.no-ip. 1040 :UBC 0 : pg_fatal("dispatching PREPARE failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1041 :CBC 1 : send_step = BI_INSERT_ROWS;
1042 : :
1043 : : /*
1044 : : * Now we start inserting. We'll be sending enough data that we could fill
1045 : : * our output buffer, so to avoid deadlocking we need to enter nonblocking
1046 : : * mode and consume input while we send more output. As results of each
1047 : : * query are processed we should pop them to allow processing of the next
1048 : : * query. There's no need to finish the pipeline before processing
1049 : : * results.
1050 : : */
1051 [ - + ]: 1 : if (PQsetnonblocking(conn, 1) != 0)
2037 alvherre@alvh.no-ip. 1052 :UBC 0 : pg_fatal("failed to set nonblocking mode: %s", PQerrorMessage(conn));
1053 : :
2037 alvherre@alvh.no-ip. 1054 [ + + ]:CBC 18651 : while (recv_step != BI_DONE)
1055 : : {
1056 : : int sock;
1057 : : fd_set input_mask;
1058 : : fd_set output_mask;
1059 : :
1060 : 18650 : sock = PQsocket(conn);
1061 : :
1062 [ - + ]: 18650 : if (sock < 0)
2037 alvherre@alvh.no-ip. 1063 :UBC 0 : break; /* shouldn't happen */
1064 : :
2037 alvherre@alvh.no-ip. 1065 [ + + ]:CBC 317050 : FD_ZERO(&input_mask);
1066 : 18650 : FD_SET(sock, &input_mask);
1067 [ + + ]: 317050 : FD_ZERO(&output_mask);
1068 : 18650 : FD_SET(sock, &output_mask);
1069 : :
1070 [ - + ]: 18650 : if (select(sock + 1, &input_mask, &output_mask, NULL, NULL) < 0)
1071 : : {
944 michael@paquier.xyz 1072 :UBC 0 : fprintf(stderr, "select() failed: %m\n");
2037 alvherre@alvh.no-ip. 1073 : 0 : exit_nicely(conn);
1074 : : }
1075 : :
1076 : : /*
1077 : : * Process any results, so we keep the server's output buffer free
1078 : : * flowing and it can continue to process input
1079 : : */
2037 alvherre@alvh.no-ip. 1080 [ + + ]:CBC 18650 : if (FD_ISSET(sock, &input_mask))
1081 : : {
1082 : 3 : PQconsumeInput(conn);
1083 : :
1084 : : /* Read until we'd block if we tried to read */
1085 [ + + + + ]: 1414 : while (!PQisBusy(conn) && recv_step < BI_DONE)
1086 : : {
1087 : : PGresult *res;
2021 tgl@sss.pgh.pa.us 1088 : 1411 : const char *cmdtag = "";
2037 alvherre@alvh.no-ip. 1089 : 1411 : const char *description = "";
1090 : : int status;
1091 : :
1092 : : /*
1093 : : * Read next result. If no more results from this query,
1094 : : * advance to the next query
1095 : : */
1096 : 1411 : res = PQgetResult(conn);
1097 [ + + ]: 1411 : if (res == NULL)
1098 : 705 : continue;
1099 : :
1100 : 706 : status = PGRES_COMMAND_OK;
1101 [ + + + + : 706 : switch (recv_step)
+ + + -
- ]
1102 : : {
1103 : 1 : case BI_BEGIN_TX:
1104 : 1 : cmdtag = "BEGIN";
1105 : 1 : recv_step++;
1106 : 1 : break;
1107 : 1 : case BI_DROP_TABLE:
1108 : 1 : cmdtag = "DROP TABLE";
1109 : 1 : recv_step++;
1110 : 1 : break;
1111 : 1 : case BI_CREATE_TABLE:
1112 : 1 : cmdtag = "CREATE TABLE";
1113 : 1 : recv_step++;
1114 : 1 : break;
1115 : 1 : case BI_PREPARE:
1116 : 1 : cmdtag = "";
1117 : 1 : description = "PREPARE";
1118 : 1 : recv_step++;
1119 : 1 : break;
1120 : 700 : case BI_INSERT_ROWS:
1121 : 700 : cmdtag = "INSERT";
1122 : 700 : rows_to_receive--;
1123 [ + + ]: 700 : if (rows_to_receive == 0)
1124 : 1 : recv_step++;
1125 : 700 : break;
1126 : 1 : case BI_COMMIT_TX:
1127 : 1 : cmdtag = "COMMIT";
1128 : 1 : recv_step++;
1129 : 1 : break;
1130 : 1 : case BI_SYNC:
1131 : 1 : cmdtag = "";
1132 : 1 : description = "SYNC";
1133 : 1 : status = PGRES_PIPELINE_SYNC;
1134 : 1 : recv_step++;
1135 : 1 : break;
2037 alvherre@alvh.no-ip. 1136 :UBC 0 : case BI_DONE:
1137 : : /* unreachable */
2031 tgl@sss.pgh.pa.us 1138 : 0 : pg_fatal("unreachable state");
1139 : : }
1140 : :
2037 alvherre@alvh.no-ip. 1141 [ - + ]:CBC 706 : if (PQresultStatus(res) != status)
2037 alvherre@alvh.no-ip. 1142 :UBC 0 : pg_fatal("%s reported status %s, expected %s\n"
1143 : : "Error message: \"%s\"",
1144 : : description, PQresStatus(PQresultStatus(res)),
1145 : : PQresStatus(status), PQerrorMessage(conn));
1146 : :
2037 alvherre@alvh.no-ip. 1147 [ - + ]:CBC 706 : if (strncmp(PQcmdStatus(res), cmdtag, strlen(cmdtag)) != 0)
2037 alvherre@alvh.no-ip. 1148 :UBC 0 : pg_fatal("%s expected command tag '%s', got '%s'",
1149 : : description, cmdtag, PQcmdStatus(res));
1150 : :
1151 : : pg_debug("Got %s OK\n", cmdtag[0] != '\0' ? cmdtag : description);
1152 : :
2037 alvherre@alvh.no-ip. 1153 :CBC 706 : PQclear(res);
1154 : : }
1155 : : }
1156 : :
1157 : : /* Write more rows and/or the end pipeline message, if needed */
1158 [ + + ]: 18650 : if (FD_ISSET(sock, &output_mask))
1159 : : {
1160 : 18649 : PQflush(conn);
1161 : :
1162 [ + + ]: 18649 : if (send_step == BI_INSERT_ROWS)
1163 : : {
2021 1164 : 700 : snprintf(insert_param_0, MAXINTLEN, "%d", rows_to_send);
1165 : : /* use up some buffer space with a wide value */
2020 1166 : 700 : snprintf(insert_param_1, MAXINT8LEN, "%lld", 1LL << 62);
1167 : :
2037 1168 [ + - ]: 700 : if (PQsendQueryPrepared(conn, "my_insert",
1169 : : 2, insert_params, NULL, NULL, 0) == 1)
1170 : : {
1171 : : pg_debug("sent row %d\n", rows_to_send);
1172 : :
1173 : 700 : rows_to_send--;
1174 [ + + ]: 700 : if (rows_to_send == 0)
1175 : 1 : send_step++;
1176 : : }
1177 : : else
1178 : : {
1179 : : /*
1180 : : * in nonblocking mode, so it's OK for an insert to fail
1181 : : * to send
1182 : : */
2037 alvherre@alvh.no-ip. 1183 :UBC 0 : fprintf(stderr, "WARNING: failed to send insert #%d: %s\n",
1184 : : rows_to_send, PQerrorMessage(conn));
1185 : : }
1186 : : }
2037 alvherre@alvh.no-ip. 1187 [ + + ]:CBC 17949 : else if (send_step == BI_COMMIT_TX)
1188 : : {
1189 [ + - ]: 1 : if (PQsendQueryParams(conn, "COMMIT",
1190 : : 0, NULL, NULL, NULL, NULL, 0) == 1)
1191 : : {
1192 : : pg_debug("sent COMMIT\n");
1193 : 1 : send_step++;
1194 : : }
1195 : : else
1196 : : {
2037 alvherre@alvh.no-ip. 1197 :UBC 0 : fprintf(stderr, "WARNING: failed to send commit: %s\n",
1198 : : PQerrorMessage(conn));
1199 : : }
1200 : : }
2037 alvherre@alvh.no-ip. 1201 [ + + ]:CBC 17948 : else if (send_step == BI_SYNC)
1202 : : {
1203 [ + - ]: 1 : if (PQpipelineSync(conn) == 1)
1204 : : {
1205 : 1 : fprintf(stdout, "pipeline sync sent\n");
1206 : 1 : send_step++;
1207 : : }
1208 : : else
1209 : : {
2037 alvherre@alvh.no-ip. 1210 :UBC 0 : fprintf(stderr, "WARNING: pipeline sync failed: %s\n",
1211 : : PQerrorMessage(conn));
1212 : : }
1213 : : }
1214 : : }
1215 : : }
1216 : :
1217 : : /* We've got the sync message and the pipeline should be done */
2037 alvherre@alvh.no-ip. 1218 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1219 :UBC 0 : pg_fatal("attempt to exit pipeline mode failed when it should've succeeded: %s",
1220 : : PQerrorMessage(conn));
1221 : :
2037 alvherre@alvh.no-ip. 1222 [ - + ]:CBC 1 : if (PQsetnonblocking(conn, 0) != 0)
2037 alvherre@alvh.no-ip. 1223 :UBC 0 : pg_fatal("failed to clear nonblocking mode: %s", PQerrorMessage(conn));
1224 : :
2037 alvherre@alvh.no-ip. 1225 :CBC 1 : fprintf(stderr, "ok\n");
1226 : 1 : }
1227 : :
1228 : : static void
1229 : 1 : test_prepared(PGconn *conn)
1230 : : {
1231 : 1 : PGresult *res = NULL;
1232 : 1 : Oid param_oids[1] = {INT4OID};
1233 : : Oid expected_oids[4];
1234 : : Oid typ;
1235 : :
1236 : 1 : fprintf(stderr, "prepared... ");
1237 : :
1238 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1239 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1240 [ - + ]:CBC 1 : if (PQsendPrepare(conn, "select_one", "SELECT $1, '42', $1::numeric, "
1241 : : "interval '1 sec'",
1242 : : 1, param_oids) != 1)
2037 alvherre@alvh.no-ip. 1243 :UBC 0 : pg_fatal("preparing query failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1244 :CBC 1 : expected_oids[0] = INT4OID;
1245 : 1 : expected_oids[1] = TEXTOID;
1246 : 1 : expected_oids[2] = NUMERICOID;
1247 : 1 : expected_oids[3] = INTERVALOID;
1248 [ - + ]: 1 : if (PQsendDescribePrepared(conn, "select_one") != 1)
2037 alvherre@alvh.no-ip. 1249 :UBC 0 : pg_fatal("failed to send describePrepared: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1250 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1251 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
1252 : :
404 tgl@sss.pgh.pa.us 1253 :CBC 1 : consume_result_status(conn, PGRES_COMMAND_OK);
1254 : :
1255 : 1 : consume_null_result(conn);
1256 : :
1257 : 1 : res = confirm_result_status(conn, PGRES_COMMAND_OK);
2037 alvherre@alvh.no-ip. 1258 [ - + ]: 1 : if (PQnfields(res) != lengthof(expected_oids))
1105 peter@eisentraut.org 1259 :UBC 0 : pg_fatal("expected %zu columns, got %d",
1260 : : lengthof(expected_oids), PQnfields(res));
2037 alvherre@alvh.no-ip. 1261 [ + + ]:CBC 5 : for (int i = 0; i < PQnfields(res); i++)
1262 : : {
1263 : 4 : typ = PQftype(res, i);
1264 [ - + ]: 4 : if (typ != expected_oids[i])
2037 alvherre@alvh.no-ip. 1265 :UBC 0 : pg_fatal("field %d: expected type %u, got %u",
1266 : : i, expected_oids[i], typ);
1267 : : }
2037 alvherre@alvh.no-ip. 1268 :CBC 1 : PQclear(res);
1269 : :
404 tgl@sss.pgh.pa.us 1270 : 1 : consume_null_result(conn);
1271 : :
1272 : 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
1273 : :
1196 michael@paquier.xyz 1274 : 1 : fprintf(stderr, "closing statement..");
1275 [ - + ]: 1 : if (PQsendClosePrepared(conn, "select_one") != 1)
1196 michael@paquier.xyz 1276 :UBC 0 : pg_fatal("PQsendClosePrepared failed: %s", PQerrorMessage(conn));
1196 michael@paquier.xyz 1277 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
1196 michael@paquier.xyz 1278 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
1279 : :
404 tgl@sss.pgh.pa.us 1280 :CBC 1 : consume_result_status(conn, PGRES_COMMAND_OK);
1281 : :
1282 : 1 : consume_null_result(conn);
1283 : :
1284 : 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
1285 : :
2037 alvherre@alvh.no-ip. 1286 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1287 :UBC 0 : pg_fatal("could not exit pipeline mode: %s", PQerrorMessage(conn));
1288 : :
1289 : : /* Now that it's closed we should get an error when describing */
1196 michael@paquier.xyz 1290 :CBC 1 : res = PQdescribePrepared(conn, "select_one");
1291 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_FATAL_ERROR)
1196 michael@paquier.xyz 1292 :UBC 0 : pg_fatal("expected FATAL_ERROR, got %s", PQresStatus(PQresultStatus(res)));
404 tgl@sss.pgh.pa.us 1293 :CBC 1 : PQclear(res);
1294 : :
1295 : : /*
1296 : : * Also test the blocking close, this should not fail since closing a
1297 : : * non-existent prepared statement is a no-op
1298 : : */
1196 michael@paquier.xyz 1299 : 1 : res = PQclosePrepared(conn, "select_one");
1300 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
1196 michael@paquier.xyz 1301 :UBC 0 : pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res)));
404 tgl@sss.pgh.pa.us 1302 :CBC 1 : PQclear(res);
1303 : :
1196 michael@paquier.xyz 1304 : 1 : fprintf(stderr, "creating portal... ");
1305 : :
404 tgl@sss.pgh.pa.us 1306 : 1 : res = PQexec(conn, "BEGIN");
1307 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
404 tgl@sss.pgh.pa.us 1308 :UBC 0 : pg_fatal("BEGIN failed: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 1309 :CBC 1 : PQclear(res);
1310 : :
1311 : 1 : res = PQexec(conn, "DECLARE cursor_one CURSOR FOR SELECT 1");
1312 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
404 tgl@sss.pgh.pa.us 1313 :UBC 0 : pg_fatal("DECLARE CURSOR failed: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 1314 :CBC 1 : PQclear(res);
1315 : :
2037 alvherre@alvh.no-ip. 1316 : 1 : PQenterPipelineMode(conn);
1317 [ - + ]: 1 : if (PQsendDescribePortal(conn, "cursor_one") != 1)
2037 alvherre@alvh.no-ip. 1318 :UBC 0 : pg_fatal("PQsendDescribePortal failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1319 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1320 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
1321 : :
404 tgl@sss.pgh.pa.us 1322 :CBC 1 : res = confirm_result_status(conn, PGRES_COMMAND_OK);
2037 alvherre@alvh.no-ip. 1323 : 1 : typ = PQftype(res, 0);
1324 [ - + ]: 1 : if (typ != INT4OID)
2037 alvherre@alvh.no-ip. 1325 :UBC 0 : pg_fatal("portal: expected type %u, got %u",
1326 : : INT4OID, typ);
2037 alvherre@alvh.no-ip. 1327 :CBC 1 : PQclear(res);
1328 : :
404 tgl@sss.pgh.pa.us 1329 : 1 : consume_null_result(conn);
1330 : :
1331 : 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
1332 : :
1196 michael@paquier.xyz 1333 : 1 : fprintf(stderr, "closing portal... ");
1334 [ - + ]: 1 : if (PQsendClosePortal(conn, "cursor_one") != 1)
1196 michael@paquier.xyz 1335 :UBC 0 : pg_fatal("PQsendClosePortal failed: %s", PQerrorMessage(conn));
1196 michael@paquier.xyz 1336 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
1196 michael@paquier.xyz 1337 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
1338 : :
404 tgl@sss.pgh.pa.us 1339 :CBC 1 : consume_result_status(conn, PGRES_COMMAND_OK);
1340 : :
1341 : 1 : consume_null_result(conn);
1342 : :
1343 : 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
1344 : :
2037 alvherre@alvh.no-ip. 1345 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1346 :UBC 0 : pg_fatal("could not exit pipeline mode: %s", PQerrorMessage(conn));
1347 : :
1348 : : /* Now that it's closed we should get an error when describing */
1196 michael@paquier.xyz 1349 :CBC 1 : res = PQdescribePortal(conn, "cursor_one");
1350 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_FATAL_ERROR)
1196 michael@paquier.xyz 1351 :UBC 0 : pg_fatal("expected FATAL_ERROR, got %s", PQresStatus(PQresultStatus(res)));
404 tgl@sss.pgh.pa.us 1352 :CBC 1 : PQclear(res);
1353 : :
1354 : : /*
1355 : : * Also test the blocking close, this should not fail since closing a
1356 : : * non-existent portal is a no-op
1357 : : */
1196 michael@paquier.xyz 1358 : 1 : res = PQclosePortal(conn, "cursor_one");
1359 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
1196 michael@paquier.xyz 1360 :UBC 0 : pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res)));
404 tgl@sss.pgh.pa.us 1361 :CBC 1 : PQclear(res);
1362 : :
2037 alvherre@alvh.no-ip. 1363 : 1 : fprintf(stderr, "ok\n");
1364 : 1 : }
1365 : :
1366 : : /*
1367 : : * Test max_protocol_version options.
1368 : : */
1369 : : static void
558 heikki.linnakangas@i 1370 : 1 : test_protocol_version(PGconn *conn)
1371 : : {
1372 : : const char **keywords;
1373 : : const char **vals;
1374 : : int nopts;
1375 : 1 : PQconninfoOption *opts = PQconninfo(conn);
1376 : : int protocol_version;
262 jchampion@postgresql 1377 : 1 : int max_protocol_version_index = -1;
1378 : : int i;
1379 : :
1380 : : /* Prepare keywords/vals arrays, copied from the existing connection. */
558 heikki.linnakangas@i 1381 : 1 : nopts = 0;
1382 [ + + ]: 53 : for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
1383 : 52 : nopts++;
1384 : 1 : nopts++; /* NULL terminator */
1385 : :
227 michael@paquier.xyz 1386 : 1 : keywords = pg_malloc0_array(const char *, nopts);
1387 : 1 : vals = pg_malloc0_array(const char *, nopts);
1388 : :
558 heikki.linnakangas@i 1389 : 1 : i = 0;
1390 [ + + ]: 53 : for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
1391 : : {
1392 : : /*
1393 : : * If the test already specified max_protocol_version, we want to
1394 : : * replace it rather than attempting to override it. This matters when
1395 : : * testing defaults, because empty option values at the end of the
1396 : : * connection string won't replace earlier settings.
1397 : : */
262 jchampion@postgresql 1398 [ + + ]: 52 : if (strcmp(opt->keyword, "max_protocol_version") == 0)
1399 : 1 : max_protocol_version_index = i;
1400 [ + + ]: 51 : else if (!opt->val)
1401 : 32 : continue;
1402 : :
1403 : 20 : keywords[i] = opt->keyword;
1404 : 20 : vals[i] = opt->val;
1405 : :
1406 : 20 : i++;
1407 : : }
1408 : :
1409 [ - + ]: 1 : Assert(max_protocol_version_index >= 0);
1410 : :
1411 : : /*
1412 : : * Test default protocol_version
1413 : : */
1414 : 1 : vals[max_protocol_version_index] = "";
1415 : 1 : conn = PQconnectdbParams(keywords, vals, false);
1416 : :
1417 [ - + ]: 1 : if (PQstatus(conn) != CONNECTION_OK)
262 jchampion@postgresql 1418 :UBC 0 : pg_fatal("Connection to database failed: %s",
1419 : : PQerrorMessage(conn));
1420 : :
262 jchampion@postgresql 1421 :CBC 1 : protocol_version = PQfullProtocolVersion(conn);
41 1422 [ - + ]: 1 : if (protocol_version != 30000)
41 jchampion@postgresql 1423 :UBC 0 : pg_fatal("expected 30000, got %d", protocol_version);
1424 : :
262 jchampion@postgresql 1425 :CBC 1 : PQfinish(conn);
1426 : :
1427 : : /*
1428 : : * Test max_protocol_version=3.0
1429 : : */
558 heikki.linnakangas@i 1430 : 1 : vals[max_protocol_version_index] = "3.0";
1431 : 1 : conn = PQconnectdbParams(keywords, vals, false);
1432 : :
1433 [ - + ]: 1 : if (PQstatus(conn) != CONNECTION_OK)
558 heikki.linnakangas@i 1434 :UBC 0 : pg_fatal("Connection to database failed: %s",
1435 : : PQerrorMessage(conn));
1436 : :
558 heikki.linnakangas@i 1437 :CBC 1 : protocol_version = PQfullProtocolVersion(conn);
1438 [ - + ]: 1 : if (protocol_version != 30000)
558 heikki.linnakangas@i 1439 :UBC 0 : pg_fatal("expected 30000, got %d", protocol_version);
1440 : :
558 heikki.linnakangas@i 1441 :CBC 1 : PQfinish(conn);
1442 : :
1443 : : /*
1444 : : * Test max_protocol_version=3.1. It's not valid, we went straight from
1445 : : * 3.0 to 3.2.
1446 : : */
1447 : 1 : vals[max_protocol_version_index] = "3.1";
1448 : 1 : conn = PQconnectdbParams(keywords, vals, false);
1449 : :
1450 [ - + ]: 1 : if (PQstatus(conn) != CONNECTION_BAD)
558 heikki.linnakangas@i 1451 :UBC 0 : pg_fatal("Connecting with max_protocol_version 3.1 should have failed.");
1452 : :
558 heikki.linnakangas@i 1453 :CBC 1 : PQfinish(conn);
1454 : :
1455 : : /*
1456 : : * Test max_protocol_version=3.2
1457 : : */
1458 : 1 : vals[max_protocol_version_index] = "3.2";
1459 : 1 : conn = PQconnectdbParams(keywords, vals, false);
1460 : :
1461 [ - + ]: 1 : if (PQstatus(conn) != CONNECTION_OK)
558 heikki.linnakangas@i 1462 :UBC 0 : pg_fatal("Connection to database failed: %s",
1463 : : PQerrorMessage(conn));
1464 : :
558 heikki.linnakangas@i 1465 :CBC 1 : protocol_version = PQfullProtocolVersion(conn);
1466 [ - + ]: 1 : if (protocol_version != 30002)
558 heikki.linnakangas@i 1467 :UBC 0 : pg_fatal("expected 30002, got %d", protocol_version);
1468 : :
558 heikki.linnakangas@i 1469 :CBC 1 : PQfinish(conn);
1470 : :
1471 : : /*
1472 : : * Test max_protocol_version=latest. 'latest' currently means '3.2'.
1473 : : */
1474 : 1 : vals[max_protocol_version_index] = "latest";
1475 : 1 : conn = PQconnectdbParams(keywords, vals, false);
1476 : :
1477 [ - + ]: 1 : if (PQstatus(conn) != CONNECTION_OK)
558 heikki.linnakangas@i 1478 :UBC 0 : pg_fatal("Connection to database failed: %s",
1479 : : PQerrorMessage(conn));
1480 : :
558 heikki.linnakangas@i 1481 :CBC 1 : protocol_version = PQfullProtocolVersion(conn);
1482 [ - + ]: 1 : if (protocol_version != 30002)
558 heikki.linnakangas@i 1483 :UBC 0 : pg_fatal("expected 30002, got %d", protocol_version);
1484 : :
558 heikki.linnakangas@i 1485 :CBC 1 : PQfinish(conn);
1486 : :
103 peter@eisentraut.org 1487 :GNC 1 : pg_free(keywords);
1488 : 1 : pg_free(vals);
404 tgl@sss.pgh.pa.us 1489 :CBC 1 : PQconninfoFree(opts);
558 heikki.linnakangas@i 1490 : 1 : }
1491 : :
1492 : : /* Notice processor: print notices, and count how many we got */
1493 : : static void
1560 alvherre@alvh.no-ip. 1494 : 1 : notice_processor(void *arg, const char *message)
1495 : : {
1242 tgl@sss.pgh.pa.us 1496 : 1 : int *n_notices = (int *) arg;
1497 : :
1560 alvherre@alvh.no-ip. 1498 : 1 : (*n_notices)++;
1499 : 1 : fprintf(stderr, "NOTICE %d: %s", *n_notices, message);
1500 : 1 : }
1501 : :
1502 : : /* Verify behavior in "idle" state */
1503 : : static void
1504 : 1 : test_pipeline_idle(PGconn *conn)
1505 : : {
1506 : 1 : int n_notices = 0;
1507 : :
1508 : 1 : fprintf(stderr, "\npipeline idle...\n");
1509 : :
1510 : 1 : PQsetNoticeProcessor(conn, notice_processor, &n_notices);
1511 : :
1512 : : /* Try to exit pipeline mode in pipeline-idle state */
1513 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
1560 alvherre@alvh.no-ip. 1514 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
1480 alvherre@alvh.no-ip. 1515 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT 1", 0, NULL, NULL, NULL, NULL, 0) != 1)
1560 alvherre@alvh.no-ip. 1516 :UBC 0 : pg_fatal("failed to send query: %s", PQerrorMessage(conn));
1560 alvherre@alvh.no-ip. 1517 :CBC 1 : PQsendFlushRequest(conn);
1518 : :
404 tgl@sss.pgh.pa.us 1519 : 1 : consume_result_status(conn, PGRES_TUPLES_OK);
1520 : :
1521 : 1 : consume_null_result(conn);
1522 : :
1480 alvherre@alvh.no-ip. 1523 [ - + ]: 1 : if (PQsendQueryParams(conn, "SELECT 2", 0, NULL, NULL, NULL, NULL, 0) != 1)
1560 alvherre@alvh.no-ip. 1524 :UBC 0 : pg_fatal("failed to send query: %s", PQerrorMessage(conn));
1560 alvherre@alvh.no-ip. 1525 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) == 1)
1560 alvherre@alvh.no-ip. 1526 :UBC 0 : pg_fatal("exiting pipeline succeeded when it shouldn't");
1560 alvherre@alvh.no-ip. 1527 [ - + ]:CBC 1 : if (strncmp(PQerrorMessage(conn), "cannot exit pipeline mode",
1528 : : strlen("cannot exit pipeline mode")) != 0)
1560 alvherre@alvh.no-ip. 1529 :UBC 0 : pg_fatal("did not get expected error; got: %s",
1530 : : PQerrorMessage(conn));
1560 alvherre@alvh.no-ip. 1531 :CBC 1 : PQsendFlushRequest(conn);
1532 : :
404 tgl@sss.pgh.pa.us 1533 : 1 : consume_result_status(conn, PGRES_TUPLES_OK);
1534 : :
1535 : 1 : consume_null_result(conn);
1536 : :
1560 alvherre@alvh.no-ip. 1537 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 1)
1560 alvherre@alvh.no-ip. 1538 :UBC 0 : pg_fatal("exiting pipeline failed: %s", PQerrorMessage(conn));
1539 : :
1560 alvherre@alvh.no-ip. 1540 [ - + ]:CBC 1 : if (n_notices > 0)
1560 alvherre@alvh.no-ip. 1541 :UBC 0 : pg_fatal("got %d notice(s)", n_notices);
1480 alvherre@alvh.no-ip. 1542 :CBC 1 : fprintf(stderr, "ok - 1\n");
1543 : :
1544 : : /* Have a WARNING in the middle of a resultset */
1560 1545 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
1560 alvherre@alvh.no-ip. 1546 :UBC 0 : pg_fatal("entering pipeline mode failed: %s", PQerrorMessage(conn));
1480 alvherre@alvh.no-ip. 1547 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT pg_catalog.pg_advisory_unlock(1,1)", 0, NULL, NULL, NULL, NULL, 0) != 1)
1560 alvherre@alvh.no-ip. 1548 :UBC 0 : pg_fatal("failed to send query: %s", PQerrorMessage(conn));
1560 alvherre@alvh.no-ip. 1549 :CBC 1 : PQsendFlushRequest(conn);
1550 : :
404 tgl@sss.pgh.pa.us 1551 : 1 : consume_result_status(conn, PGRES_TUPLES_OK);
1552 : :
1560 alvherre@alvh.no-ip. 1553 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 1)
1560 alvherre@alvh.no-ip. 1554 :UBC 0 : pg_fatal("failed to exit pipeline mode: %s", PQerrorMessage(conn));
1480 alvherre@alvh.no-ip. 1555 :CBC 1 : fprintf(stderr, "ok - 2\n");
1560 1556 : 1 : }
1557 : :
1558 : : static void
2037 1559 : 1 : test_simple_pipeline(PGconn *conn)
1560 : : {
1561 : 1 : const char *dummy_params[1] = {"1"};
1562 : 1 : Oid dummy_param_oids[1] = {INT4OID};
1563 : :
1564 : 1 : fprintf(stderr, "simple pipeline... ");
1565 : :
1566 : : /*
1567 : : * Enter pipeline mode and dispatch a set of operations, which we'll then
1568 : : * process the results of as they come in.
1569 : : *
1570 : : * For a simple case we should be able to do this without interim
1571 : : * processing of results since our output buffer will give us enough slush
1572 : : * to work with and we won't block on sending. So blocking mode is fine.
1573 : : */
1574 [ - + ]: 1 : if (PQisnonblocking(conn))
2037 alvherre@alvh.no-ip. 1575 :UBC 0 : pg_fatal("Expected blocking connection mode");
1576 : :
2037 alvherre@alvh.no-ip. 1577 [ - + ]:CBC 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1578 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
1579 : :
2037 alvherre@alvh.no-ip. 1580 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT $1",
1581 : : 1, dummy_param_oids, dummy_params,
1582 : : NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 1583 :UBC 0 : pg_fatal("dispatching SELECT failed: %s", PQerrorMessage(conn));
1584 : :
2037 alvherre@alvh.no-ip. 1585 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) != 0)
2037 alvherre@alvh.no-ip. 1586 :UBC 0 : pg_fatal("exiting pipeline mode with work in progress should fail, but succeeded");
1587 : :
2037 alvherre@alvh.no-ip. 1588 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1589 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
1590 : :
404 tgl@sss.pgh.pa.us 1591 :CBC 1 : consume_result_status(conn, PGRES_TUPLES_OK);
1592 : :
1593 : 1 : consume_null_result(conn);
1594 : :
1595 : : /*
1596 : : * Even though we've processed the result there's still a sync to come and
1597 : : * we can't exit pipeline mode yet
1598 : : */
2037 alvherre@alvh.no-ip. 1599 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 0)
2037 alvherre@alvh.no-ip. 1600 :UBC 0 : pg_fatal("exiting pipeline mode after query but before sync succeeded incorrectly");
1601 : :
404 tgl@sss.pgh.pa.us 1602 :CBC 1 : consume_result_status(conn, PGRES_PIPELINE_SYNC);
1603 : :
1604 : 1 : consume_null_result(conn);
1605 : :
1606 : : /* We're still in pipeline mode... */
2037 alvherre@alvh.no-ip. 1607 [ - + ]: 1 : if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 1608 :UBC 0 : pg_fatal("Fell out of pipeline mode somehow");
1609 : :
1610 : : /* ... until we end it, which we can safely do now */
2037 alvherre@alvh.no-ip. 1611 [ - + ]:CBC 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1612 :UBC 0 : pg_fatal("attempt to exit pipeline mode failed when it should've succeeded: %s",
1613 : : PQerrorMessage(conn));
1614 : :
2037 alvherre@alvh.no-ip. 1615 [ - + ]:CBC 1 : if (PQpipelineStatus(conn) != PQ_PIPELINE_OFF)
2037 alvherre@alvh.no-ip. 1616 :UBC 0 : pg_fatal("Exiting pipeline mode didn't seem to work");
1617 : :
2037 alvherre@alvh.no-ip. 1618 :CBC 1 : fprintf(stderr, "ok\n");
1619 : 1 : }
1620 : :
1621 : : static void
1622 : 1 : test_singlerowmode(PGconn *conn)
1623 : : {
1624 : : PGresult *res;
1625 : : int i;
1626 : 1 : bool pipeline_ended = false;
1627 : :
1628 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1629 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s",
1630 : : PQerrorMessage(conn));
1631 : :
1632 : : /* One series of three commands, using single-row mode for the first two. */
2037 alvherre@alvh.no-ip. 1633 [ + + ]:CBC 4 : for (i = 0; i < 3; i++)
1634 : : {
1635 : : char *param[1];
1636 : :
1637 : 3 : param[0] = psprintf("%d", 44 + i);
1638 : :
1639 [ - + ]: 3 : if (PQsendQueryParams(conn,
1640 : : "SELECT generate_series(42, $1)",
1641 : : 1,
1642 : : NULL,
1643 : : (const char *const *) param,
1644 : : NULL,
1645 : : NULL,
1646 : : 0) != 1)
2037 alvherre@alvh.no-ip. 1647 :UBC 0 : pg_fatal("failed to send query: %s",
1648 : : PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1649 :CBC 3 : pfree(param[0]);
1650 : : }
1651 [ - + ]: 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1652 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
1653 : :
2037 alvherre@alvh.no-ip. 1654 [ + + ]:CBC 5 : for (i = 0; !pipeline_ended; i++)
1655 : : {
1656 : 4 : bool first = true;
1657 : : bool saw_ending_tuplesok;
1658 : 4 : bool isSingleTuple = false;
1659 : :
1660 : : /* Set single row mode for only first 2 SELECT queries */
1661 [ + + ]: 4 : if (i < 2)
1662 : : {
1663 [ - + ]: 2 : if (PQsetSingleRowMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1664 :UBC 0 : pg_fatal("PQsetSingleRowMode() failed for i=%d", i);
1665 : : }
1666 : :
1667 : : /* Consume rows for this query */
2037 alvherre@alvh.no-ip. 1668 :CBC 4 : saw_ending_tuplesok = false;
1669 [ + + ]: 14 : while ((res = PQgetResult(conn)) != NULL)
1670 : : {
1671 : 11 : ExecStatusType est = PQresultStatus(res);
1672 : :
1673 [ + + ]: 11 : if (est == PGRES_PIPELINE_SYNC)
1674 : : {
1675 : 1 : fprintf(stderr, "end of pipeline reached\n");
1676 : 1 : pipeline_ended = true;
1677 : 1 : PQclear(res);
1678 [ - + ]: 1 : if (i != 3)
2037 alvherre@alvh.no-ip. 1679 :UBC 0 : pg_fatal("Expected three results, got %d", i);
2037 alvherre@alvh.no-ip. 1680 :CBC 1 : break;
1681 : : }
1682 : :
1683 : : /* Expect SINGLE_TUPLE for queries 0 and 1, TUPLES_OK for 2 */
1684 [ + + ]: 10 : if (first)
1685 : : {
1686 [ + + - + ]: 3 : if (i <= 1 && est != PGRES_SINGLE_TUPLE)
2037 alvherre@alvh.no-ip. 1687 :UBC 0 : pg_fatal("Expected PGRES_SINGLE_TUPLE for query %d, got %s",
1688 : : i, PQresStatus(est));
2037 alvherre@alvh.no-ip. 1689 [ + + - + ]:CBC 3 : if (i >= 2 && est != PGRES_TUPLES_OK)
2037 alvherre@alvh.no-ip. 1690 :UBC 0 : pg_fatal("Expected PGRES_TUPLES_OK for query %d, got %s",
1691 : : i, PQresStatus(est));
2037 alvherre@alvh.no-ip. 1692 :CBC 3 : first = false;
1693 : : }
1694 : :
1695 : 10 : fprintf(stderr, "Result status %s for query %d", PQresStatus(est), i);
1696 [ + + - ]: 10 : switch (est)
1697 : : {
1698 : 3 : case PGRES_TUPLES_OK:
1699 : 3 : fprintf(stderr, ", tuples: %d\n", PQntuples(res));
1700 : 3 : saw_ending_tuplesok = true;
1701 [ + + ]: 3 : if (isSingleTuple)
1702 : : {
1703 [ + - ]: 2 : if (PQntuples(res) == 0)
1704 : 2 : fprintf(stderr, "all tuples received in query %d\n", i);
1705 : : else
2037 alvherre@alvh.no-ip. 1706 :UBC 0 : pg_fatal("Expected to follow PGRES_SINGLE_TUPLE, but received PGRES_TUPLES_OK directly instead");
1707 : : }
2037 alvherre@alvh.no-ip. 1708 :CBC 3 : break;
1709 : :
1710 : 7 : case PGRES_SINGLE_TUPLE:
1711 : 7 : isSingleTuple = true;
1712 : 7 : fprintf(stderr, ", %d tuple: %s\n", PQntuples(res), PQgetvalue(res, 0, 0));
1713 : 7 : break;
1714 : :
2037 alvherre@alvh.no-ip. 1715 :UBC 0 : default:
1716 : 0 : pg_fatal("unexpected");
1717 : : }
2037 alvherre@alvh.no-ip. 1718 :CBC 10 : PQclear(res);
1719 : : }
1720 [ + + - + ]: 4 : if (!pipeline_ended && !saw_ending_tuplesok)
2037 alvherre@alvh.no-ip. 1721 :UBC 0 : pg_fatal("didn't get expected terminating TUPLES_OK");
1722 : : }
1723 : :
1724 : : /*
1725 : : * Now issue one command, get its results in with single-row mode, then
1726 : : * issue another command, and get its results in normal mode; make sure
1727 : : * the single-row mode flag is reset as expected.
1728 : : */
1459 alvherre@alvh.no-ip. 1729 [ - + ]:CBC 1 : if (PQsendQueryParams(conn, "SELECT generate_series(0, 0)",
1730 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
1459 alvherre@alvh.no-ip. 1731 :UBC 0 : pg_fatal("failed to send query: %s",
1732 : : PQerrorMessage(conn));
1459 alvherre@alvh.no-ip. 1733 [ - + ]:CBC 1 : if (PQsendFlushRequest(conn) != 1)
1459 alvherre@alvh.no-ip. 1734 :UBC 0 : pg_fatal("failed to send flush request");
1459 alvherre@alvh.no-ip. 1735 [ - + ]:CBC 1 : if (PQsetSingleRowMode(conn) != 1)
1459 alvherre@alvh.no-ip. 1736 :UBC 0 : pg_fatal("PQsetSingleRowMode() failed");
1737 : :
404 tgl@sss.pgh.pa.us 1738 :CBC 1 : consume_result_status(conn, PGRES_SINGLE_TUPLE);
1739 : :
1740 : 1 : consume_result_status(conn, PGRES_TUPLES_OK);
1741 : :
1742 : 1 : consume_null_result(conn);
1743 : :
1459 alvherre@alvh.no-ip. 1744 [ - + ]: 1 : if (PQsendQueryParams(conn, "SELECT 1",
1745 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
1459 alvherre@alvh.no-ip. 1746 :UBC 0 : pg_fatal("failed to send query: %s",
1747 : : PQerrorMessage(conn));
1459 alvherre@alvh.no-ip. 1748 [ - + ]:CBC 1 : if (PQsendFlushRequest(conn) != 1)
1459 alvherre@alvh.no-ip. 1749 :UBC 0 : pg_fatal("failed to send flush request");
1750 : :
404 tgl@sss.pgh.pa.us 1751 :CBC 1 : consume_result_status(conn, PGRES_TUPLES_OK);
1752 : :
1753 : 1 : consume_null_result(conn);
1754 : :
1755 : : /*
1756 : : * Try chunked mode as well; make sure that it correctly delivers a
1757 : : * partial final chunk.
1758 : : */
919 1759 [ - + ]: 1 : if (PQsendQueryParams(conn, "SELECT generate_series(1, 5)",
1760 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
919 tgl@sss.pgh.pa.us 1761 :UBC 0 : pg_fatal("failed to send query: %s",
1762 : : PQerrorMessage(conn));
919 tgl@sss.pgh.pa.us 1763 [ - + ]:CBC 1 : if (PQsendFlushRequest(conn) != 1)
919 tgl@sss.pgh.pa.us 1764 :UBC 0 : pg_fatal("failed to send flush request");
919 tgl@sss.pgh.pa.us 1765 [ - + ]:CBC 1 : if (PQsetChunkedRowsMode(conn, 3) != 1)
919 tgl@sss.pgh.pa.us 1766 :UBC 0 : pg_fatal("PQsetChunkedRowsMode() failed");
1767 : :
404 tgl@sss.pgh.pa.us 1768 :CBC 1 : res = confirm_result_status(conn, PGRES_TUPLES_CHUNK);
919 1769 [ - + ]: 1 : if (PQntuples(res) != 3)
919 tgl@sss.pgh.pa.us 1770 :UBC 0 : pg_fatal("Expected 3 rows, got %d", PQntuples(res));
404 tgl@sss.pgh.pa.us 1771 :CBC 1 : PQclear(res);
1772 : :
1773 : 1 : res = confirm_result_status(conn, PGRES_TUPLES_CHUNK);
919 1774 [ - + ]: 1 : if (PQntuples(res) != 2)
919 tgl@sss.pgh.pa.us 1775 :UBC 0 : pg_fatal("Expected 2 rows, got %d", PQntuples(res));
404 tgl@sss.pgh.pa.us 1776 :CBC 1 : PQclear(res);
1777 : :
1778 : 1 : res = confirm_result_status(conn, PGRES_TUPLES_OK);
919 1779 [ - + ]: 1 : if (PQntuples(res) != 0)
919 tgl@sss.pgh.pa.us 1780 :UBC 0 : pg_fatal("Expected 0 rows, got %d", PQntuples(res));
404 tgl@sss.pgh.pa.us 1781 :CBC 1 : PQclear(res);
1782 : :
1783 : 1 : consume_null_result(conn);
1784 : :
2037 alvherre@alvh.no-ip. 1785 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1786 :UBC 0 : pg_fatal("failed to end pipeline mode: %s", PQerrorMessage(conn));
1787 : :
1560 alvherre@alvh.no-ip. 1788 :CBC 1 : fprintf(stderr, "ok\n");
2037 1789 : 1 : }
1790 : :
1791 : : /*
1792 : : * Simple test to verify that a pipeline is discarded as a whole when there's
1793 : : * an error, ignoring transaction commands.
1794 : : */
1795 : : static void
1796 : 1 : test_transaction(PGconn *conn)
1797 : : {
1798 : : PGresult *res;
1799 : : bool expect_null;
1800 : 1 : int num_syncs = 0;
1801 : :
1802 : 1 : res = PQexec(conn, "DROP TABLE IF EXISTS pq_pipeline_tst;"
1803 : : "CREATE TABLE pq_pipeline_tst (id int)");
1804 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2037 alvherre@alvh.no-ip. 1805 :UBC 0 : pg_fatal("failed to create test table: %s",
1806 : : PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1807 :CBC 1 : PQclear(res);
1808 : :
1809 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1810 :UBC 0 : pg_fatal("failed to enter pipeline mode: %s",
1811 : : PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1812 [ - + ]:CBC 1 : if (PQsendPrepare(conn, "rollback", "ROLLBACK", 0, NULL) != 1)
2037 alvherre@alvh.no-ip. 1813 :UBC 0 : pg_fatal("could not send prepare on pipeline: %s",
1814 : : PQerrorMessage(conn));
1815 : :
2037 alvherre@alvh.no-ip. 1816 [ - + ]:CBC 1 : if (PQsendQueryParams(conn,
1817 : : "BEGIN",
1818 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 1819 :UBC 0 : pg_fatal("failed to send query: %s",
1820 : : PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1821 [ - + ]:CBC 1 : if (PQsendQueryParams(conn,
1822 : : "SELECT 0/0",
1823 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 1824 :UBC 0 : pg_fatal("failed to send query: %s",
1825 : : PQerrorMessage(conn));
1826 : :
1827 : : /*
1828 : : * send a ROLLBACK using a prepared stmt. Doesn't work because we need to
1829 : : * get out of the pipeline-aborted state first.
1830 : : */
2037 alvherre@alvh.no-ip. 1831 [ - + ]:CBC 1 : if (PQsendQueryPrepared(conn, "rollback", 0, NULL, NULL, NULL, 1) != 1)
2037 alvherre@alvh.no-ip. 1832 :UBC 0 : pg_fatal("failed to execute prepared: %s",
1833 : : PQerrorMessage(conn));
1834 : :
1835 : : /* This insert fails because we're in pipeline-aborted state */
2037 alvherre@alvh.no-ip. 1836 [ - + ]:CBC 1 : if (PQsendQueryParams(conn,
1837 : : "INSERT INTO pq_pipeline_tst VALUES (1)",
1838 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 1839 :UBC 0 : pg_fatal("failed to send query: %s",
1840 : : PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1841 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1842 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1843 :CBC 1 : num_syncs++;
1844 : :
1845 : : /*
1846 : : * This insert fails even though the pipeline got a SYNC, because we're in
1847 : : * an aborted transaction
1848 : : */
1849 [ - + ]: 1 : if (PQsendQueryParams(conn,
1850 : : "INSERT INTO pq_pipeline_tst VALUES (2)",
1851 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 1852 :UBC 0 : pg_fatal("failed to send query: %s",
1853 : : PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1854 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1855 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1856 :CBC 1 : num_syncs++;
1857 : :
1858 : : /*
1859 : : * Send ROLLBACK using prepared stmt. This one works because we just did
1860 : : * PQpipelineSync above.
1861 : : */
1862 [ - + ]: 1 : if (PQsendQueryPrepared(conn, "rollback", 0, NULL, NULL, NULL, 1) != 1)
2037 alvherre@alvh.no-ip. 1863 :UBC 0 : pg_fatal("failed to execute prepared: %s",
1864 : : PQerrorMessage(conn));
1865 : :
1866 : : /*
1867 : : * Now that we're out of a transaction and in pipeline-good mode, this
1868 : : * insert works
1869 : : */
2037 alvherre@alvh.no-ip. 1870 [ - + ]:CBC 1 : if (PQsendQueryParams(conn,
1871 : : "INSERT INTO pq_pipeline_tst VALUES (3)",
1872 : : 0, NULL, NULL, NULL, NULL, 0) != 1)
2037 alvherre@alvh.no-ip. 1873 :UBC 0 : pg_fatal("failed to send query: %s",
1874 : : PQerrorMessage(conn));
1875 : : /* Send two syncs now -- match up to SYNC messages below */
2037 alvherre@alvh.no-ip. 1876 [ - + ]:CBC 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1877 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1878 :CBC 1 : num_syncs++;
1879 [ - + ]: 1 : if (PQpipelineSync(conn) != 1)
2037 alvherre@alvh.no-ip. 1880 :UBC 0 : pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1881 :CBC 1 : num_syncs++;
1882 : :
1883 : 1 : expect_null = false;
1884 : 1 : for (int i = 0;; i++)
1885 : 19 : {
1886 : : ExecStatusType restype;
1887 : :
1888 : 20 : res = PQgetResult(conn);
1889 [ + + ]: 20 : if (res == NULL)
1890 : : {
1891 : 8 : printf("%d: got NULL result\n", i);
1892 [ - + ]: 8 : if (!expect_null)
2037 alvherre@alvh.no-ip. 1893 :UBC 0 : pg_fatal("did not expect NULL here");
2037 alvherre@alvh.no-ip. 1894 :CBC 8 : expect_null = false;
1895 : 8 : continue;
1896 : : }
1897 : 12 : restype = PQresultStatus(res);
1898 : 12 : printf("%d: got status %s", i, PQresStatus(restype));
1899 [ - + ]: 12 : if (expect_null)
2037 alvherre@alvh.no-ip. 1900 :UBC 0 : pg_fatal("expected NULL");
2037 alvherre@alvh.no-ip. 1901 [ + + ]:CBC 12 : if (restype == PGRES_FATAL_ERROR)
1902 : 2 : printf("; error: %s", PQerrorMessage(conn));
1903 [ + + ]: 10 : else if (restype == PGRES_PIPELINE_ABORTED)
1904 : : {
1905 : 2 : printf(": command didn't run because pipeline aborted\n");
1906 : : }
1907 : : else
1908 : 8 : printf("\n");
1909 : 12 : PQclear(res);
1910 : :
1911 [ + + ]: 12 : if (restype == PGRES_PIPELINE_SYNC)
1912 : 4 : num_syncs--;
1913 : : else
1914 : 8 : expect_null = true;
1915 [ + + ]: 12 : if (num_syncs <= 0)
1916 : 1 : break;
1917 : : }
1918 : :
404 tgl@sss.pgh.pa.us 1919 : 1 : consume_null_result(conn);
1920 : :
2037 alvherre@alvh.no-ip. 1921 [ - + ]: 1 : if (PQexitPipelineMode(conn) != 1)
2037 alvherre@alvh.no-ip. 1922 :UBC 0 : pg_fatal("failed to end pipeline mode: %s", PQerrorMessage(conn));
1923 : :
1924 : : /* We expect to find one tuple containing the value "3" */
2037 alvherre@alvh.no-ip. 1925 :CBC 1 : res = PQexec(conn, "SELECT * FROM pq_pipeline_tst");
1926 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
2037 alvherre@alvh.no-ip. 1927 :UBC 0 : pg_fatal("failed to obtain result: %s", PQerrorMessage(conn));
2037 alvherre@alvh.no-ip. 1928 [ - + ]:CBC 1 : if (PQntuples(res) != 1)
2037 alvherre@alvh.no-ip. 1929 :UBC 0 : pg_fatal("did not get 1 tuple");
2037 alvherre@alvh.no-ip. 1930 [ - + ]:CBC 1 : if (strcmp(PQgetvalue(res, 0, 0), "3") != 0)
2037 alvherre@alvh.no-ip. 1931 :UBC 0 : pg_fatal("did not get expected tuple");
2037 alvherre@alvh.no-ip. 1932 :CBC 1 : PQclear(res);
1933 : :
1934 : 1 : fprintf(stderr, "ok\n");
1935 : 1 : }
1936 : :
1937 : : /*
1938 : : * In this test mode we send a stream of queries, with one in the middle
1939 : : * causing an error. Verify that we can still send some more after the
1940 : : * error and have libpq work properly.
1941 : : */
1942 : : static void
1921 1943 : 1 : test_uniqviol(PGconn *conn)
1944 : : {
1945 : 1 : int sock = PQsocket(conn);
1946 : : PGresult *res;
1947 : 1 : Oid paramTypes[2] = {INT8OID, INT8OID};
1948 : : const char *paramValues[2];
1949 : : char paramValue0[MAXINT8LEN];
1950 : : char paramValue1[MAXINT8LEN];
1951 : 1 : int ctr = 0;
1952 : 1 : int numsent = 0;
1953 : 1 : int results = 0;
1954 : 1 : bool read_done = false;
1955 : 1 : bool write_done = false;
1956 : 1 : bool error_sent = false;
1957 : 1 : bool got_error = false;
1958 : 1 : int switched = 0;
1959 : 1 : int socketful = 0;
1960 : : fd_set in_fds;
1961 : : fd_set out_fds;
1962 : :
1963 : 1 : fprintf(stderr, "uniqviol ...");
1964 : :
1965 : 1 : PQsetnonblocking(conn, 1);
1966 : :
1967 : 1 : paramValues[0] = paramValue0;
1968 : 1 : paramValues[1] = paramValue1;
1969 : 1 : sprintf(paramValue1, "42");
1970 : :
1971 : 1 : res = PQexec(conn, "drop table if exists ppln_uniqviol;"
1972 : : "create table ppln_uniqviol(id bigint primary key, idata bigint)");
1973 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
1921 alvherre@alvh.no-ip. 1974 :UBC 0 : pg_fatal("failed to create table: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 1975 :CBC 1 : PQclear(res);
1976 : :
1921 alvherre@alvh.no-ip. 1977 : 1 : res = PQexec(conn, "begin");
1978 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
1921 alvherre@alvh.no-ip. 1979 :UBC 0 : pg_fatal("failed to begin transaction: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 1980 :CBC 1 : PQclear(res);
1981 : :
1921 alvherre@alvh.no-ip. 1982 : 1 : res = PQprepare(conn, "insertion",
1983 : : "insert into ppln_uniqviol values ($1, $2) returning id",
1984 : : 2, paramTypes);
404 tgl@sss.pgh.pa.us 1985 [ - + ]: 1 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
1921 alvherre@alvh.no-ip. 1986 :UBC 0 : pg_fatal("failed to prepare query: %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 1987 :CBC 1 : PQclear(res);
1988 : :
1921 alvherre@alvh.no-ip. 1989 [ - + ]: 1 : if (PQenterPipelineMode(conn) != 1)
1921 alvherre@alvh.no-ip. 1990 :UBC 0 : pg_fatal("failed to enter pipeline mode");
1991 : :
1921 alvherre@alvh.no-ip. 1992 [ + - ]:CBC 8 : while (!read_done)
1993 : : {
1994 : : /*
1995 : : * Avoid deadlocks by reading everything the server has sent before
1996 : : * sending anything. (Special precaution is needed here to process
1997 : : * PQisBusy before testing the socket for read-readiness, because the
1998 : : * socket does not turn read-ready after "sending" queries in aborted
1999 : : * pipeline mode.)
2000 : : */
2001 [ + + ]: 606 : while (PQisBusy(conn) == 0)
2002 : : {
2003 : : bool new_error;
2004 : :
2005 [ + + ]: 601 : if (results >= numsent)
2006 : : {
2007 [ - + ]: 1 : if (write_done)
1921 alvherre@alvh.no-ip. 2008 :UBC 0 : read_done = true;
1921 alvherre@alvh.no-ip. 2009 :CBC 1 : break;
2010 : : }
2011 : :
2012 : 600 : res = PQgetResult(conn);
2013 : 600 : new_error = process_result(conn, res, results, numsent);
2014 [ + + - + ]: 600 : if (new_error && got_error)
1921 alvherre@alvh.no-ip. 2015 :UBC 0 : pg_fatal("got two errors");
1921 alvherre@alvh.no-ip. 2016 :CBC 600 : got_error |= new_error;
2017 [ + + ]: 600 : if (results++ >= numsent - 1)
2018 : : {
2019 [ + + ]: 2 : if (write_done)
2020 : 1 : read_done = true;
2021 : 2 : break;
2022 : : }
2023 : : }
2024 : :
2025 [ + + ]: 8 : if (read_done)
2026 : 1 : break;
2027 : :
2028 [ + + ]: 119 : FD_ZERO(&out_fds);
2029 : 7 : FD_SET(sock, &out_fds);
2030 : :
2031 [ + + ]: 119 : FD_ZERO(&in_fds);
2032 : 7 : FD_SET(sock, &in_fds);
2033 : :
2034 [ - + - + ]: 7 : if (select(sock + 1, &in_fds, write_done ? NULL : &out_fds, NULL, NULL) == -1)
2035 : : {
1921 alvherre@alvh.no-ip. 2036 [ # # ]:UBC 0 : if (errno == EINTR)
2037 : 0 : continue;
2038 : 0 : pg_fatal("select() failed: %m");
2039 : : }
2040 : :
1921 alvherre@alvh.no-ip. 2041 [ + + - + ]:CBC 7 : if (FD_ISSET(sock, &in_fds) && PQconsumeInput(conn) == 0)
1921 alvherre@alvh.no-ip. 2042 :UBC 0 : pg_fatal("PQconsumeInput failed: %s", PQerrorMessage(conn));
2043 : :
2044 : : /*
2045 : : * If the socket is writable and we haven't finished sending queries,
2046 : : * send some.
2047 : : */
1921 alvherre@alvh.no-ip. 2048 [ + - + + ]:CBC 7 : if (!write_done && FD_ISSET(sock, &out_fds))
2049 : : {
2050 : : for (;;)
2051 : 597 : {
2052 : : int flush;
2053 : :
2054 : : /*
2055 : : * provoke uniqueness violation exactly once after having
2056 : : * switched to read mode.
2057 : : */
2058 [ + + + + : 600 : if (switched >= 1 && !error_sent && ctr % socketful >= socketful / 2)
+ + ]
2059 : : {
2060 : 1 : sprintf(paramValue0, "%d", numsent / 2);
2061 : 1 : fprintf(stderr, "E");
2062 : 1 : error_sent = true;
2063 : : }
2064 : : else
2065 : : {
2066 : 599 : fprintf(stderr, ".");
2067 : 599 : sprintf(paramValue0, "%d", ctr++);
2068 : : }
2069 : :
2070 [ - + ]: 600 : if (PQsendQueryPrepared(conn, "insertion", 2, paramValues, NULL, NULL, 0) != 1)
1921 alvherre@alvh.no-ip. 2071 :UBC 0 : pg_fatal("failed to execute prepared query: %s", PQerrorMessage(conn));
1921 alvherre@alvh.no-ip. 2072 :CBC 600 : numsent++;
2073 : :
2074 : : /* Are we done writing? */
2075 [ + + + + : 600 : if (socketful != 0 && numsent % socketful == 42 && error_sent)
+ + ]
2076 : : {
2077 [ - + ]: 1 : if (PQsendFlushRequest(conn) != 1)
1921 alvherre@alvh.no-ip. 2078 :UBC 0 : pg_fatal("failed to send flush request");
1921 alvherre@alvh.no-ip. 2079 :CBC 1 : write_done = true;
2080 : 1 : fprintf(stderr, "\ndone writing\n");
2081 : 1 : PQflush(conn);
2082 : 1 : break;
2083 : : }
2084 : :
2085 : : /* is the outgoing socket full? */
2086 : 599 : flush = PQflush(conn);
2087 [ - + ]: 599 : if (flush == -1)
1921 alvherre@alvh.no-ip. 2088 :UBC 0 : pg_fatal("failed to flush: %s", PQerrorMessage(conn));
1921 alvherre@alvh.no-ip. 2089 [ + + ]:CBC 599 : if (flush == 1)
2090 : : {
2091 [ + + ]: 2 : if (socketful == 0)
2092 : 1 : socketful = numsent;
2093 : 2 : fprintf(stderr, "\nswitch to reading\n");
2094 : 2 : switched++;
2095 : 2 : break;
2096 : : }
2097 : : }
2098 : : }
2099 : : }
2100 : :
2101 [ - + ]: 1 : if (!got_error)
1921 alvherre@alvh.no-ip. 2102 :UBC 0 : pg_fatal("did not get expected error");
2103 : :
1921 alvherre@alvh.no-ip. 2104 :CBC 1 : fprintf(stderr, "ok\n");
2105 : 1 : }
2106 : :
2107 : : /*
2108 : : * Subroutine for test_uniqviol; given a PGresult, print it out and consume
2109 : : * the expected NULL that should follow it.
2110 : : *
2111 : : * Returns true if we read a fatal error message, otherwise false.
2112 : : */
2113 : : static bool
2114 : 600 : process_result(PGconn *conn, PGresult *res, int results, int numsent)
2115 : : {
2116 : 600 : bool got_error = false;
2117 : :
2118 [ - + ]: 600 : if (res == NULL)
1921 alvherre@alvh.no-ip. 2119 :UBC 0 : pg_fatal("got unexpected NULL");
2120 : :
1921 alvherre@alvh.no-ip. 2121 [ + + + - ]:CBC 600 : switch (PQresultStatus(res))
2122 : : {
2123 : 1 : case PGRES_FATAL_ERROR:
2124 : 1 : got_error = true;
2125 : 1 : fprintf(stderr, "result %d/%d (error): %s\n", results, numsent, PQerrorMessage(conn));
2126 : 1 : PQclear(res);
404 tgl@sss.pgh.pa.us 2127 : 1 : consume_null_result(conn);
1921 alvherre@alvh.no-ip. 2128 : 1 : break;
2129 : :
2130 : 418 : case PGRES_TUPLES_OK:
2131 : 418 : fprintf(stderr, "result %d/%d: %s\n", results, numsent, PQgetvalue(res, 0, 0));
2132 : 418 : PQclear(res);
404 tgl@sss.pgh.pa.us 2133 : 418 : consume_null_result(conn);
1921 alvherre@alvh.no-ip. 2134 : 418 : break;
2135 : :
2136 : 181 : case PGRES_PIPELINE_ABORTED:
2137 : 181 : fprintf(stderr, "result %d/%d: pipeline aborted\n", results, numsent);
404 tgl@sss.pgh.pa.us 2138 : 181 : PQclear(res);
2139 : 181 : consume_null_result(conn);
1921 alvherre@alvh.no-ip. 2140 : 181 : break;
2141 : :
1921 alvherre@alvh.no-ip. 2142 :UBC 0 : default:
2143 : 0 : pg_fatal("got unexpected %s", PQresStatus(PQresultStatus(res)));
2144 : : }
2145 : :
1921 alvherre@alvh.no-ip. 2146 :CBC 600 : return got_error;
2147 : : }
2148 : :
2149 : :
2150 : : static void
2037 alvherre@alvh.no-ip. 2151 :UBC 0 : usage(const char *progname)
2152 : : {
2153 : 0 : fprintf(stderr, "%s tests libpq's pipeline mode.\n\n", progname);
2154 : 0 : fprintf(stderr, "Usage:\n");
2022 2155 : 0 : fprintf(stderr, " %s [OPTION] tests\n", progname);
2020 2156 : 0 : fprintf(stderr, " %s [OPTION] TESTNAME [CONNINFO]\n", progname);
2022 2157 : 0 : fprintf(stderr, "\nOptions:\n");
2158 : 0 : fprintf(stderr, " -t TRACEFILE generate a libpq trace to TRACEFILE\n");
2020 2159 : 0 : fprintf(stderr, " -r NUMROWS use NUMROWS as the test size\n");
2037 2160 : 0 : }
2161 : :
2162 : : static void
2037 alvherre@alvh.no-ip. 2163 :CBC 1 : print_test_list(void)
2164 : : {
945 2165 : 1 : printf("cancel\n");
10 michael@paquier.xyz 2166 : 1 : printf("describe_fetch\n");
2037 alvherre@alvh.no-ip. 2167 : 1 : printf("disallowed_in_pipeline\n");
2168 : 1 : printf("multi_pipelines\n");
1931 2169 : 1 : printf("nosync\n");
2037 2170 : 1 : printf("pipeline_abort\n");
1560 2171 : 1 : printf("pipeline_idle\n");
2037 2172 : 1 : printf("pipelined_insert\n");
2173 : 1 : printf("prepared\n");
558 heikki.linnakangas@i 2174 : 1 : printf("protocol_version\n");
2037 alvherre@alvh.no-ip. 2175 : 1 : printf("simple_pipeline\n");
2176 : 1 : printf("singlerow\n");
2177 : 1 : printf("transaction\n");
1921 2178 : 1 : printf("uniqviol\n");
2037 2179 : 1 : }
2180 : :
2181 : : int
2182 : 16 : main(int argc, char **argv)
2183 : : {
2184 : 16 : const char *conninfo = "";
2185 : : PGconn *conn;
404 tgl@sss.pgh.pa.us 2186 : 16 : FILE *trace = NULL;
2187 : : char *testname;
2037 alvherre@alvh.no-ip. 2188 : 16 : int numrows = 10000;
2189 : : PGresult *res;
2190 : : int c;
2191 : :
1400 peter@eisentraut.org 2192 [ + + ]: 55 : while ((c = getopt(argc, argv, "r:t:")) != -1)
2193 : : {
2022 alvherre@alvh.no-ip. 2194 [ + + - ]: 23 : switch (c)
2195 : : {
2020 2196 : 14 : case 'r': /* numrows */
2197 : 14 : errno = 0;
2198 : 14 : numrows = strtol(optarg, NULL, 10);
2199 [ + - - + ]: 14 : if (errno != 0 || numrows <= 0)
2200 : : {
2020 alvherre@alvh.no-ip. 2201 :UBC 0 : fprintf(stderr, "couldn't parse \"%s\" as a positive integer\n",
2202 : : optarg);
2203 : 0 : exit(1);
2204 : : }
2020 alvherre@alvh.no-ip. 2205 :CBC 14 : break;
1400 peter@eisentraut.org 2206 : 9 : case 't': /* trace file */
2207 : 9 : tracefile = pg_strdup(optarg);
2208 : 9 : break;
2209 : : }
2210 : : }
2211 : :
2022 alvherre@alvh.no-ip. 2212 [ + - ]: 16 : if (optind < argc)
2213 : : {
2020 2214 : 16 : testname = pg_strdup(argv[optind]);
2022 2215 : 16 : optind++;
2216 : : }
2217 : : else
2218 : : {
2037 alvherre@alvh.no-ip. 2219 :UBC 0 : usage(argv[0]);
2220 : 0 : exit(1);
2221 : : }
2222 : :
2022 alvherre@alvh.no-ip. 2223 [ + + ]:CBC 16 : if (strcmp(testname, "tests") == 0)
2224 : : {
2225 : 1 : print_test_list();
2226 : 1 : exit(0);
2227 : : }
2228 : :
2229 [ + - ]: 15 : if (optind < argc)
2230 : : {
2020 2231 : 15 : conninfo = pg_strdup(argv[optind]);
2022 2232 : 15 : optind++;
2233 : : }
2234 : :
2235 : : /* Make a connection to the database */
2037 2236 : 15 : conn = PQconnectdb(conninfo);
2237 [ - + ]: 15 : if (PQstatus(conn) != CONNECTION_OK)
2238 : : {
2037 alvherre@alvh.no-ip. 2239 :UBC 0 : fprintf(stderr, "Connection to database failed: %s\n",
2240 : : PQerrorMessage(conn));
2241 : 0 : exit_nicely(conn);
2242 : : }
2243 : :
2021 alvherre@alvh.no-ip. 2244 :CBC 15 : res = PQexec(conn, "SET lc_messages TO \"C\"");
2245 [ - + ]: 15 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
878 peter@eisentraut.org 2246 :UBC 0 : pg_fatal("failed to set \"lc_messages\": %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 2247 :CBC 15 : PQclear(res);
1335 drowley@postgresql.o 2248 : 15 : res = PQexec(conn, "SET debug_parallel_query = off");
2021 alvherre@alvh.no-ip. 2249 [ - + ]: 15 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
878 peter@eisentraut.org 2250 :UBC 0 : pg_fatal("failed to set \"debug_parallel_query\": %s", PQerrorMessage(conn));
404 tgl@sss.pgh.pa.us 2251 :CBC 15 : PQclear(res);
2252 : :
2253 : : /* Set the trace file, if requested */
2022 alvherre@alvh.no-ip. 2254 [ + + ]: 15 : if (tracefile != NULL)
2255 : : {
1560 2256 [ - + ]: 9 : if (strcmp(tracefile, "-") == 0)
1560 alvherre@alvh.no-ip. 2257 :UBC 0 : trace = stdout;
2258 : : else
1560 alvherre@alvh.no-ip. 2259 :CBC 9 : trace = fopen(tracefile, "w");
2022 2260 [ - + ]: 9 : if (trace == NULL)
2022 alvherre@alvh.no-ip. 2261 :UBC 0 : pg_fatal("could not open file \"%s\": %m", tracefile);
2262 : :
2263 : : /* Make it line-buffered */
2020 alvherre@alvh.no-ip. 2264 :CBC 9 : setvbuf(trace, NULL, PG_IOLBF, 0);
2265 : :
2022 2266 : 9 : PQtrace(conn, trace);
1950 noah@leadboat.com 2267 : 9 : PQsetTraceFlags(conn,
2268 : : PQTRACE_SUPPRESS_TIMESTAMPS | PQTRACE_REGRESS_MODE);
2269 : : }
2270 : :
945 alvherre@alvh.no-ip. 2271 [ + + ]: 15 : if (strcmp(testname, "cancel") == 0)
2272 : 2 : test_cancel(conn);
10 michael@paquier.xyz 2273 [ + + ]: 13 : else if (strcmp(testname, "describe_fetch") == 0)
2274 : 1 : test_describe_fetch(conn);
945 alvherre@alvh.no-ip. 2275 [ + + ]: 12 : else if (strcmp(testname, "disallowed_in_pipeline") == 0)
2037 2276 : 1 : test_disallowed_in_pipeline(conn);
2022 2277 [ + + ]: 11 : else if (strcmp(testname, "multi_pipelines") == 0)
2037 2278 : 1 : test_multi_pipelines(conn);
1931 2279 [ + + ]: 10 : else if (strcmp(testname, "nosync") == 0)
2280 : 1 : test_nosync(conn);
2022 2281 [ + + ]: 9 : else if (strcmp(testname, "pipeline_abort") == 0)
2037 2282 : 1 : test_pipeline_abort(conn);
1560 2283 [ + + ]: 8 : else if (strcmp(testname, "pipeline_idle") == 0)
2284 : 1 : test_pipeline_idle(conn);
2022 2285 [ + + ]: 7 : else if (strcmp(testname, "pipelined_insert") == 0)
2037 2286 : 1 : test_pipelined_insert(conn, numrows);
2022 2287 [ + + ]: 6 : else if (strcmp(testname, "prepared") == 0)
2037 2288 : 1 : test_prepared(conn);
558 heikki.linnakangas@i 2289 [ + + ]: 5 : else if (strcmp(testname, "protocol_version") == 0)
2290 : 1 : test_protocol_version(conn);
2022 alvherre@alvh.no-ip. 2291 [ + + ]: 4 : else if (strcmp(testname, "simple_pipeline") == 0)
2037 2292 : 1 : test_simple_pipeline(conn);
2022 2293 [ + + ]: 3 : else if (strcmp(testname, "singlerow") == 0)
2037 2294 : 1 : test_singlerowmode(conn);
2022 2295 [ + + ]: 2 : else if (strcmp(testname, "transaction") == 0)
2037 2296 : 1 : test_transaction(conn);
1921 2297 [ + - ]: 1 : else if (strcmp(testname, "uniqviol") == 0)
2298 : 1 : test_uniqviol(conn);
2299 : : else
2300 : : {
2022 alvherre@alvh.no-ip. 2301 :UBC 0 : fprintf(stderr, "\"%s\" is not a recognized test name\n", testname);
2037 2302 : 0 : exit(1);
2303 : : }
2304 : :
2305 : : /* close the connection to the database and cleanup */
2037 alvherre@alvh.no-ip. 2306 :CBC 15 : PQfinish(conn);
2307 : :
404 tgl@sss.pgh.pa.us 2308 [ + + + - ]: 15 : if (trace && trace != stdout)
2309 : 9 : fclose(trace);
2310 : :
2037 alvherre@alvh.no-ip. 2311 : 15 : return 0;
2312 : : }
|