Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_regress --- regression test driver
4 : : *
5 : : * This is a C implementation of the previous shell script for running
6 : : * the regression tests, and should be mostly compatible with it.
7 : : * Initial author of C translation: Magnus Hagander
8 : : *
9 : : * This code is released under the terms of the PostgreSQL License.
10 : : *
11 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
12 : : * Portions Copyright (c) 1994, Regents of the University of California
13 : : *
14 : : * src/test/regress/pg_regress.c
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : :
19 : : #include "postgres_fe.h"
20 : :
21 : : #include <ctype.h>
22 : : #include <sys/resource.h>
23 : : #include <sys/stat.h>
24 : : #include <sys/time.h>
25 : : #include <sys/wait.h>
26 : : #include <signal.h>
27 : : #include <unistd.h>
28 : :
29 : : #include "common/logging.h"
30 : : #include "common/restricted_token.h"
31 : : #include "common/string.h"
32 : : #include "common/username.h"
33 : : #include "getopt_long.h"
34 : : #include "lib/stringinfo.h"
35 : : #include "libpq-fe.h"
36 : : #include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
37 : : #include "pg_config_paths.h"
38 : : #include "pg_regress.h"
39 : : #include "portability/instr_time.h"
40 : :
41 : : /* for resultmap we need a list of pairs of strings */
42 : : typedef struct _resultmap
43 : : {
44 : : char *test;
45 : : char *type;
46 : : char *resultfile;
47 : : struct _resultmap *next;
48 : : } _resultmap;
49 : :
50 : : /*
51 : : * Values obtained from Makefile.
52 : : */
53 : : char *host_platform = HOST_TUPLE;
54 : :
55 : : #ifndef WIN32 /* not used in WIN32 case */
56 : : static char *shellprog = SHELLPROG;
57 : : #endif
58 : :
59 : : /*
60 : : * On Windows we use -w in diff switches to avoid problems with inconsistent
61 : : * newline representation. The actual result files will generally have
62 : : * Windows-style newlines, but the comparison files might or might not.
63 : : */
64 : : #ifndef WIN32
65 : : const char *basic_diff_opts = "";
66 : : const char *pretty_diff_opts = "-U3";
67 : : #else
68 : : const char *basic_diff_opts = "-w";
69 : : const char *pretty_diff_opts = "-w -U3";
70 : : #endif
71 : :
72 : : /*
73 : : * The width of the testname field when printing to ensure vertical alignment
74 : : * of test runtimes. This number is somewhat arbitrarily chosen to match the
75 : : * older pre-TAP output format.
76 : : */
77 : : #define TESTNAME_WIDTH 36
78 : :
79 : : /*
80 : : * The number times per second that pg_regress checks to see if the test
81 : : * instance server has started and is available for connection.
82 : : */
83 : : #define WAIT_TICKS_PER_SECOND 20
84 : :
85 : : typedef enum TAPtype
86 : : {
87 : : DIAG = 0,
88 : : BAIL,
89 : : NOTE,
90 : : NOTE_DETAIL,
91 : : NOTE_END,
92 : : TEST_STATUS,
93 : : PLAN,
94 : : NONE,
95 : : } TAPtype;
96 : :
97 : : /* options settable from command line */
98 : : _stringlist *dblist = NULL;
99 : : bool debug = false;
100 : : char *inputdir = ".";
101 : : char *outputdir = ".";
102 : : char *expecteddir = ".";
103 : : char *bindir = PGBINDIR;
104 : : char *launcher = NULL;
105 : : static _stringlist *loadextension = NULL;
106 : : static int max_connections = 0;
107 : : static int max_concurrent_tests = 0;
108 : : static char *encoding = NULL;
109 : : static _stringlist *schedulelist = NULL;
110 : : static _stringlist *extra_tests = NULL;
111 : : static char *temp_instance = NULL;
112 : : static _stringlist *temp_configs = NULL;
113 : : static bool nolocale = false;
114 : : static bool use_existing = false;
115 : : static char *hostname = NULL;
116 : : static int port = -1;
117 : : static char portstr[16];
118 : : static bool port_specified_by_user = false;
119 : : static char *dlpath = PKGLIBDIR;
120 : : static char *user = NULL;
121 : : static _stringlist *extraroles = NULL;
122 : : static char *config_auth_datadir = NULL;
123 : :
124 : : /* internal variables */
125 : : static const char *progname;
126 : : static char *logfilename;
127 : : static FILE *logfile;
128 : : static char *difffilename;
129 : : static const char *sockdir;
130 : : static const char *temp_sockdir;
131 : : static char sockself[MAXPGPATH];
132 : : static char socklock[MAXPGPATH];
133 : : static StringInfo failed_tests = NULL;
134 : : static bool in_note = false;
135 : :
136 : : static _resultmap *resultmap = NULL;
137 : :
138 : : static PID_TYPE postmaster_pid = INVALID_PID;
139 : : static bool postmaster_running = false;
140 : :
141 : : static int success_count = 0;
142 : : static int fail_count = 0;
143 : :
144 : : static bool directory_exists(const char *dir);
145 : : static void make_directory(const char *dir);
146 : :
147 : : static void test_status_print(bool ok, const char *testname, double runtime, bool parallel);
148 : : static void test_status_ok(const char *testname, double runtime, bool parallel);
149 : : static void test_status_failed(const char *testname, double runtime, bool parallel);
150 : : static void bail_out(bool noatexit, const char *fmt,...) pg_attribute_printf(2, 3);
151 : : static void emit_tap_output(TAPtype type, const char *fmt,...) pg_attribute_printf(2, 3);
152 : : static void emit_tap_output_v(TAPtype type, const char *fmt, va_list argp) pg_attribute_printf(2, 0);
153 : :
154 : : static StringInfo psql_start_command(void);
155 : : static void psql_add_command(StringInfo buf, const char *query,...) pg_attribute_printf(2, 3);
156 : : static void psql_end_command(StringInfo buf, const char *database);
157 : :
158 : : /*
159 : : * Convenience macros for printing TAP output with a more shorthand syntax
160 : : * aimed at making the code more readable.
161 : : */
162 : : #define plan(x) emit_tap_output(PLAN, "1..%i", (x))
163 : : #define note(...) emit_tap_output(NOTE, __VA_ARGS__)
164 : : #define note_detail(...) emit_tap_output(NOTE_DETAIL, __VA_ARGS__)
165 : : #define diag(...) emit_tap_output(DIAG, __VA_ARGS__)
166 : : #define note_end() emit_tap_output(NOTE_END, "\n");
167 : : #define bail_noatexit(...) bail_out(true, __VA_ARGS__)
168 : : #define bail(...) bail_out(false, __VA_ARGS__)
169 : :
170 : : /*
171 : : * allow core files if possible.
172 : : */
173 : : #if defined(HAVE_GETRLIMIT)
174 : : static void
6309 andrew@dunslane.net 175 :CBC 87 : unlimit_core_size(void)
176 : : {
177 : : struct rlimit lim;
178 : :
5995 bruce@momjian.us 179 : 87 : getrlimit(RLIMIT_CORE, &lim);
6309 andrew@dunslane.net 180 [ - + ]: 87 : if (lim.rlim_max == 0)
181 : : {
380 dgustafsson@postgres 182 :UBC 0 : diag("could not set core size: disallowed by hard limit");
6309 andrew@dunslane.net 183 : 0 : return;
184 : : }
6309 andrew@dunslane.net 185 [ - + - - ]:CBC 87 : else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
186 : : {
187 : 87 : lim.rlim_cur = lim.rlim_max;
5995 bruce@momjian.us 188 : 87 : setrlimit(RLIMIT_CORE, &lim);
189 : : }
190 : : }
191 : : #endif
192 : :
193 : :
194 : : /*
195 : : * Add an item at the end of a stringlist.
196 : : */
197 : : void
3631 198 : 3544 : add_stringlist_item(_stringlist **listhead, const char *str)
199 : : {
2784 tgl@sss.pgh.pa.us 200 : 3544 : _stringlist *newentry = pg_malloc(sizeof(_stringlist));
201 : : _stringlist *oldentry;
202 : :
203 : 3544 : newentry->str = pg_strdup(str);
6479 204 : 3544 : newentry->next = NULL;
205 [ + + ]: 3544 : if (*listhead == NULL)
206 : 2928 : *listhead = newentry;
207 : : else
208 : : {
209 [ + + ]: 2748 : for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
210 : : /* skip */ ;
211 : 616 : oldentry->next = newentry;
212 : : }
213 : 3544 : }
214 : :
215 : : /*
216 : : * Free a stringlist.
217 : : */
218 : : static void
3631 bruce@momjian.us 219 : 3018 : free_stringlist(_stringlist **listhead)
220 : : {
6151 magnus@hagander.net 221 [ + - + + ]: 3018 : if (listhead == NULL || *listhead == NULL)
222 : 786 : return;
223 [ + + ]: 2232 : if ((*listhead)->next != NULL)
224 : 385 : free_stringlist(&((*listhead)->next));
225 : 2232 : free((*listhead)->str);
226 : 2232 : free(*listhead);
227 : 2232 : *listhead = NULL;
228 : : }
229 : :
230 : : /*
231 : : * Split a delimited string into a stringlist
232 : : */
233 : : static void
3631 bruce@momjian.us 234 : 108 : split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
235 : : {
2784 tgl@sss.pgh.pa.us 236 : 108 : char *sc = pg_strdup(s);
5995 bruce@momjian.us 237 : 108 : char *token = strtok(sc, delim);
238 : :
6151 magnus@hagander.net 239 [ + + ]: 222 : while (token)
240 : : {
241 : 114 : add_stringlist_item(listhead, token);
242 : 114 : token = strtok(NULL, delim);
243 : : }
244 : 108 : free(sc);
245 : 108 : }
246 : :
247 : : /*
248 : : * Bailing out is for unrecoverable errors which prevents further testing to
249 : : * occur and after which the test run should be aborted. By passing noatexit
250 : : * as true the process will terminate with _exit(2) and skipping registered
251 : : * exit handlers, thus avoid any risk of bottomless recursion calls to exit.
252 : : */
253 : : static void
380 dgustafsson@postgres 254 :UBC 0 : bail_out(bool noatexit, const char *fmt,...)
255 : : {
256 : : va_list ap;
257 : :
6479 tgl@sss.pgh.pa.us 258 : 0 : va_start(ap, fmt);
380 dgustafsson@postgres 259 : 0 : emit_tap_output_v(BAIL, fmt, ap);
6479 tgl@sss.pgh.pa.us 260 : 0 : va_end(ap);
261 : :
380 dgustafsson@postgres 262 [ # # ]: 0 : if (noatexit)
263 : 0 : _exit(2);
264 : :
265 : 0 : exit(2);
266 : : }
267 : :
268 : : /*
269 : : * Print the result of a test run and associated metadata like runtime. Care
270 : : * is taken to align testnames and runtimes vertically to ensure the output
271 : : * is human readable while still TAP compliant. Tests run in parallel are
272 : : * prefixed with a '+' and sequential tests with a '-'. This distinction was
273 : : * previously indicated by 'test' prefixing sequential tests while parallel
274 : : * tests were indented by four leading spaces. The meson TAP parser consumes
275 : : * leading space however, so a non-whitespace prefix of the same length is
276 : : * required for both.
277 : : */
278 : : static void
380 dgustafsson@postgres 279 :CBC 1152 : test_status_print(bool ok, const char *testname, double runtime, bool parallel)
280 : : {
281 : 1152 : int testnumber = fail_count + success_count;
282 : :
283 : : /*
284 : : * Testnumbers are padded to 5 characters to ensure that testnames align
285 : : * vertically (assuming at most 9999 tests). Testnames are prefixed with
286 : : * a leading character to indicate being run in parallel or not. A leading
287 : : * '+' indicates a parallel test, '-' indicates a single test.
288 : : */
289 [ + + + - : 1152 : emit_tap_output(TEST_STATUS, "%sok %-5i%*s %c %-*s %8.0f ms",
+ - ]
290 : : (ok ? "" : "not "),
291 : : testnumber,
292 : : /* If ok, indent with four spaces matching "not " */
293 : : (ok ? (int) strlen("not ") : 0), "",
294 : : /* Prefix a parallel test '+' and a single test with '-' */
295 : : (parallel ? '+' : '-'),
296 : : /* Testnames are padded to align runtimes */
297 : : TESTNAME_WIDTH, testname,
298 : : runtime);
299 : 1152 : }
300 : :
301 : : static void
302 : 1152 : test_status_ok(const char *testname, double runtime, bool parallel)
303 : : {
304 : 1152 : success_count++;
305 : :
306 : 1152 : test_status_print(true, testname, runtime, parallel);
6479 tgl@sss.pgh.pa.us 307 : 1152 : }
308 : :
309 : : static void
380 dgustafsson@postgres 310 :UBC 0 : test_status_failed(const char *testname, double runtime, bool parallel)
311 : : {
312 : : /*
313 : : * Save failed tests in a buffer such that we can print a summary at the
314 : : * end with diag() to ensure it's shown even under test harnesses.
315 : : */
316 [ # # ]: 0 : if (!failed_tests)
317 : 0 : failed_tests = makeStringInfo();
318 : : else
319 : 0 : appendStringInfoChar(failed_tests, ',');
320 : :
321 : 0 : appendStringInfo(failed_tests, " %s", testname);
322 : :
323 : 0 : fail_count++;
324 : :
325 : 0 : test_status_print(false, testname, runtime, parallel);
326 : 0 : }
327 : :
328 : :
329 : : static void
380 dgustafsson@postgres 330 :CBC 2251 : emit_tap_output(TAPtype type, const char *fmt,...)
331 : : {
332 : : va_list argp;
333 : :
334 : 2251 : va_start(argp, fmt);
335 : 2251 : emit_tap_output_v(type, fmt, argp);
336 : 2251 : va_end(argp);
337 : 2251 : }
338 : :
339 : : static void
340 : 2251 : emit_tap_output_v(TAPtype type, const char *fmt, va_list argp)
341 : : {
342 : : va_list argp_logfile;
343 : : FILE *fp;
344 : : int save_errno;
345 : :
346 : : /*
347 : : * The fprintf() calls used to output TAP-protocol elements might clobber
348 : : * errno, so save it here and restore it before vfprintf()-ing the user's
349 : : * format string, in case it contains %m placeholders.
350 : : */
10 michael@paquier.xyz 351 :GNC 2251 : save_errno = errno;
352 : :
353 : : /*
354 : : * Diagnostic output will be hidden by prove unless printed to stderr. The
355 : : * Bail message is also printed to stderr to aid debugging under a harness
356 : : * which might otherwise not emit such an important message.
357 : : */
380 dgustafsson@postgres 358 [ + - - + ]:CBC 2251 : if (type == DIAG || type == BAIL)
380 dgustafsson@postgres 359 :UBC 0 : fp = stderr;
360 : : else
380 dgustafsson@postgres 361 :CBC 2251 : fp = stdout;
362 : :
363 : : /*
364 : : * If we are ending a note_detail line we can avoid further processing and
365 : : * immediately return following a newline.
366 : : */
367 [ + + ]: 2251 : if (type == NOTE_END)
368 : : {
369 : 54 : in_note = false;
370 : 54 : fprintf(fp, "\n");
371 [ + - ]: 54 : if (logfile)
372 : 54 : fprintf(logfile, "\n");
373 : 54 : return;
374 : : }
375 : :
376 : : /* Make a copy of the va args for printing to the logfile */
377 : 2197 : va_copy(argp_logfile, argp);
378 : :
379 : : /*
380 : : * Non-protocol output such as diagnostics or notes must be prefixed by a
381 : : * '#' character. We print the Bail message like this too.
382 : : */
383 [ + + + - : 2197 : if ((type == NOTE || type == DIAG || type == BAIL)
+ - ]
384 [ + + + + ]: 1938 : || (type == NOTE_DETAIL && !in_note))
385 : : {
386 : 313 : fprintf(fp, "# ");
387 [ + - ]: 313 : if (logfile)
388 : 313 : fprintf(logfile, "# ");
389 : : }
10 michael@paquier.xyz 390 :GNC 2197 : errno = save_errno;
380 dgustafsson@postgres 391 :CBC 2197 : vfprintf(fp, fmt, argp);
6479 tgl@sss.pgh.pa.us 392 [ + - ]: 2197 : if (logfile)
393 : : {
10 michael@paquier.xyz 394 :GNC 2197 : errno = save_errno;
380 dgustafsson@postgres 395 :CBC 2197 : vfprintf(logfile, fmt, argp_logfile);
396 : : }
397 : :
398 : : /*
399 : : * If we are entering into a note with more details to follow, register
400 : : * that the leading '#' has been printed such that subsequent details
401 : : * aren't prefixed as well.
402 : : */
403 [ + + ]: 2197 : if (type == NOTE_DETAIL)
404 : 699 : in_note = true;
405 : :
406 : : /*
407 : : * If this was a Bail message, the bail protocol message must go to stdout
408 : : * separately.
409 : : */
410 [ - + ]: 2197 : if (type == BAIL)
411 : : {
380 dgustafsson@postgres 412 :UBC 0 : fprintf(stdout, "Bail out!");
413 [ # # ]: 0 : if (logfile)
414 : 0 : fprintf(logfile, "Bail out!");
415 : : }
416 : :
380 dgustafsson@postgres 417 :CBC 2197 : va_end(argp_logfile);
418 : :
419 [ + + ]: 2197 : if (type != NOTE_DETAIL)
420 : : {
421 : 1498 : fprintf(fp, "\n");
422 [ + - ]: 1498 : if (logfile)
423 : 1498 : fprintf(logfile, "\n");
424 : : }
425 : 2197 : fflush(NULL);
426 : : }
427 : :
428 : : /*
429 : : * shut down temp postmaster
430 : : */
431 : : static void
6479 tgl@sss.pgh.pa.us 432 : 437 : stop_postmaster(void)
433 : : {
434 [ + + ]: 437 : if (postmaster_running)
435 : : {
436 : : /* We use pg_ctl to issue the kill and wait for stop */
437 : : char buf[MAXPGPATH * 2];
438 : : int r;
439 : :
6479 tgl@sss.pgh.pa.us 440 :UBC 0 : snprintf(buf, sizeof(buf),
441 : : "\"%s%spg_ctl\" stop -D \"%s/data\" -s",
3279 peter_e@gmx.net 442 [ - + ]:CBC 85 : bindir ? bindir : "",
443 [ - + ]: 85 : bindir ? "/" : "",
444 : : temp_instance);
594 tgl@sss.pgh.pa.us 445 : 85 : fflush(NULL);
5619 peter_e@gmx.net 446 : 85 : r = system(buf);
447 [ - + ]: 85 : if (r != 0)
448 : : {
449 : : /* Not using the normal bail() as we want _exit */
380 dgustafsson@postgres 450 :UBC 0 : bail_noatexit(_("could not stop postmaster: exit code was %d"), r);
451 : : }
452 : :
6479 tgl@sss.pgh.pa.us 453 :CBC 85 : postmaster_running = false;
454 : : }
455 : 437 : }
456 : :
457 : : /*
458 : : * Remove the socket temporary directory. pg_regress never waits for a
459 : : * postmaster exit, so it is indeterminate whether the postmaster has yet to
460 : : * unlink the socket and lock file. Unlink them here so we can proceed to
461 : : * remove the directory. Ignore errors; leaking a temporary directory is
462 : : * unimportant. This can run from a signal handler. The code is not
463 : : * acceptable in a Windows signal handler (see initdb.c:trapsig()), but
464 : : * on Windows, pg_regress does not use Unix sockets by default.
465 : : */
466 : : static void
3592 noah@leadboat.com 467 : 85 : remove_temp(void)
468 : : {
469 [ - + ]: 85 : Assert(temp_sockdir);
470 : 85 : unlink(sockself);
471 : 85 : unlink(socklock);
472 : 85 : rmdir(temp_sockdir);
473 : 85 : }
474 : :
475 : : /*
476 : : * Signal handler that calls remove_temp() and reraises the signal.
477 : : */
478 : : static void
578 tgl@sss.pgh.pa.us 479 :UBC 0 : signal_remove_temp(SIGNAL_ARGS)
480 : : {
3592 noah@leadboat.com 481 : 0 : remove_temp();
482 : :
578 tgl@sss.pgh.pa.us 483 : 0 : pqsignal(postgres_signal_arg, SIG_DFL);
484 : 0 : raise(postgres_signal_arg);
3592 noah@leadboat.com 485 : 0 : }
486 : :
487 : : /*
488 : : * Create a temporary directory suitable for the server's Unix-domain socket.
489 : : * The directory will have mode 0700 or stricter, so no other OS user can open
490 : : * our socket to exploit our use of trust authentication. Most systems
491 : : * constrain the length of socket paths well below _POSIX_PATH_MAX, so we
492 : : * place the directory under /tmp rather than relative to the possibly-deep
493 : : * current working directory.
494 : : *
495 : : * Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits
496 : : * testing to work in builds that relocate it to a directory not writable to
497 : : * the build/test user.
498 : : */
499 : : static const char *
3592 noah@leadboat.com 500 :CBC 85 : make_temp_sockdir(void)
501 : : {
1477 peter@eisentraut.org 502 [ - + ]: 85 : char *template = psprintf("%s/pg_regress-XXXXXX",
1477 peter@eisentraut.org 503 :UBC 0 : getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp");
504 : :
3592 noah@leadboat.com 505 :CBC 85 : temp_sockdir = mkdtemp(template);
506 [ - + ]: 85 : if (temp_sockdir == NULL)
10 michael@paquier.xyz 507 :UNC 0 : bail("could not create directory \"%s\": %m", template);
508 : :
509 : : /* Stage file names for remove_temp(). Unsafe in a signal handler. */
3592 noah@leadboat.com 510 [ - + - + ]:CBC 85 : UNIXSOCK_PATH(sockself, port, temp_sockdir);
511 : 85 : snprintf(socklock, sizeof(socklock), "%s.lock", sockself);
512 : :
513 : : /* Remove the directory during clean exit. */
514 : 85 : atexit(remove_temp);
515 : :
516 : : /*
517 : : * Remove the directory before dying to the usual signals. Omit SIGQUIT,
518 : : * preserving it as a quick, untidy exit.
519 : : */
520 : 85 : pqsignal(SIGHUP, signal_remove_temp);
521 : 85 : pqsignal(SIGINT, signal_remove_temp);
522 : 85 : pqsignal(SIGPIPE, signal_remove_temp);
523 : 85 : pqsignal(SIGTERM, signal_remove_temp);
524 : :
525 : 85 : return temp_sockdir;
526 : : }
527 : :
528 : : /*
529 : : * Check whether string matches pattern
530 : : *
531 : : * In the original shell script, this function was implemented using expr(1),
532 : : * which provides basic regular expressions restricted to match starting at
533 : : * the string start (in conventional regex terms, there's an implicit "^"
534 : : * at the start of the pattern --- but no implicit "$" at the end).
535 : : *
536 : : * For now, we only support "." and ".*" as non-literal metacharacters,
537 : : * because that's all that anyone has found use for in resultmap. This
538 : : * code could be extended if more functionality is needed.
539 : : */
540 : : static bool
6479 tgl@sss.pgh.pa.us 541 : 24 : string_matches_pattern(const char *str, const char *pattern)
542 : : {
543 [ + - + - ]: 42 : while (*str && *pattern)
544 : : {
545 [ + + + - ]: 42 : if (*pattern == '.' && pattern[1] == '*')
546 : : {
547 : 18 : pattern += 2;
548 : : /* Trailing .* matches everything. */
549 [ - + ]: 18 : if (*pattern == '\0')
6479 tgl@sss.pgh.pa.us 550 :UBC 0 : return true;
551 : :
552 : : /*
553 : : * Otherwise, scan for a text position at which we can match the
554 : : * rest of the pattern.
555 : : */
6479 tgl@sss.pgh.pa.us 556 [ + + ]:CBC 186 : while (*str)
557 : : {
558 : : /*
559 : : * Optimization to prevent most recursion: don't recurse
560 : : * unless first pattern char might match this text char.
561 : : */
562 [ + + - + ]: 168 : if (*str == *pattern || *pattern == '.')
563 : : {
564 [ - + ]: 18 : if (string_matches_pattern(str, pattern))
6479 tgl@sss.pgh.pa.us 565 :UBC 0 : return true;
566 : : }
567 : :
6479 tgl@sss.pgh.pa.us 568 :CBC 168 : str++;
569 : : }
570 : :
571 : : /*
572 : : * End of text with no match.
573 : : */
574 : 18 : return false;
575 : : }
576 [ + - + + ]: 24 : else if (*pattern != '.' && *str != *pattern)
577 : : {
578 : : /*
579 : : * Not the single-character wildcard and no explicit match? Then
580 : : * time to quit...
581 : : */
582 : 6 : return false;
583 : : }
584 : :
585 : 18 : str++;
586 : 18 : pattern++;
587 : : }
588 : :
6479 tgl@sss.pgh.pa.us 589 [ # # ]:UBC 0 : if (*pattern == '\0')
590 : 0 : return true; /* end of pattern, so declare match */
591 : :
592 : : /* End of input string. Do we have matching pattern remaining? */
593 [ # # # # ]: 0 : while (*pattern == '.' && pattern[1] == '*')
594 : 0 : pattern += 2;
595 [ # # ]: 0 : if (*pattern == '\0')
596 : 0 : return true; /* end of pattern, so declare match */
597 : :
598 : 0 : return false;
599 : : }
600 : :
601 : : /*
602 : : * Scan resultmap file to find which platform-specific expected files to use.
603 : : *
604 : : * The format of each line of the file is
605 : : * testname/hostplatformpattern=substitutefile
606 : : * where the hostplatformpattern is evaluated per the rules of expr(1),
607 : : * namely, it is a standard regular expression with an implicit ^ at the start.
608 : : * (We currently support only a very limited subset of regular expressions,
609 : : * see string_matches_pattern() above.) What hostplatformpattern will be
610 : : * matched against is the config.guess output. (In the shell-script version,
611 : : * we also provided an indication of whether gcc or another compiler was in
612 : : * use, but that facility isn't used anymore.)
613 : : */
614 : : static void
6479 tgl@sss.pgh.pa.us 615 :CBC 87 : load_resultmap(void)
616 : : {
617 : : char buf[MAXPGPATH];
618 : : FILE *f;
619 : :
620 : : /* scan the file ... */
621 : 87 : snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
6402 bruce@momjian.us 622 : 87 : f = fopen(buf, "r");
6479 tgl@sss.pgh.pa.us 623 [ + + ]: 87 : if (!f)
624 : : {
625 : : /* OK if it doesn't exist, else complain */
626 [ + - ]: 84 : if (errno == ENOENT)
627 : 84 : return;
10 michael@paquier.xyz 628 :UNC 0 : bail("could not open file \"%s\" for reading: %m", buf);
629 : : }
630 : :
6479 tgl@sss.pgh.pa.us 631 [ + + ]:CBC 12 : while (fgets(buf, sizeof(buf), f))
632 : : {
633 : : char *platform;
634 : : char *file_type;
635 : : char *expected;
636 : : int i;
637 : :
638 : : /* strip trailing whitespace, especially the newline */
639 : 6 : i = strlen(buf);
6402 bruce@momjian.us 640 [ + - + + ]: 12 : while (i > 0 && isspace((unsigned char) buf[i - 1]))
6479 tgl@sss.pgh.pa.us 641 : 6 : buf[--i] = '\0';
642 : :
643 : : /* parse out the line fields */
6151 magnus@hagander.net 644 : 6 : file_type = strchr(buf, ':');
645 [ - + ]: 6 : if (!file_type)
646 : : {
380 dgustafsson@postgres 647 :UBC 0 : bail("incorrectly formatted resultmap entry: %s", buf);
648 : : }
6151 magnus@hagander.net 649 :CBC 6 : *file_type++ = '\0';
650 : :
651 : 6 : platform = strchr(file_type, ':');
6479 tgl@sss.pgh.pa.us 652 [ - + ]: 6 : if (!platform)
653 : : {
380 dgustafsson@postgres 654 :UBC 0 : bail("incorrectly formatted resultmap entry: %s", buf);
655 : : }
6479 tgl@sss.pgh.pa.us 656 :CBC 6 : *platform++ = '\0';
657 : 6 : expected = strchr(platform, '=');
658 [ - + ]: 6 : if (!expected)
659 : : {
380 dgustafsson@postgres 660 :UBC 0 : bail("incorrectly formatted resultmap entry: %s", buf);
661 : : }
6479 tgl@sss.pgh.pa.us 662 :CBC 6 : *expected++ = '\0';
663 : :
664 : : /*
665 : : * if it's for current platform, save it in resultmap list. Note: by
666 : : * adding at the front of the list, we ensure that in ambiguous cases,
667 : : * the last match in the resultmap file is used. This mimics the
668 : : * behavior of the old shell script.
669 : : */
670 [ - + ]: 6 : if (string_matches_pattern(host_platform, platform))
671 : : {
2784 tgl@sss.pgh.pa.us 672 :UBC 0 : _resultmap *entry = pg_malloc(sizeof(_resultmap));
673 : :
674 : 0 : entry->test = pg_strdup(buf);
675 : 0 : entry->type = pg_strdup(file_type);
676 : 0 : entry->resultfile = pg_strdup(expected);
6479 677 : 0 : entry->next = resultmap;
678 : 0 : resultmap = entry;
679 : : }
680 : : }
6479 tgl@sss.pgh.pa.us 681 :CBC 3 : fclose(f);
682 : : }
683 : :
684 : : /*
685 : : * Check in resultmap if we should be looking at a different file
686 : : */
687 : : static
688 : : const char *
5995 bruce@momjian.us 689 : 1282 : get_expectfile(const char *testname, const char *file)
690 : : {
691 : : char *file_type;
692 : : _resultmap *rm;
693 : :
694 : : /*
695 : : * Determine the file type from the file name. This is just what is
696 : : * following the last dot in the file name.
697 : : */
6151 magnus@hagander.net 698 [ + - - + ]: 1282 : if (!file || !(file_type = strrchr(file, '.')))
6151 magnus@hagander.net 699 :UBC 0 : return NULL;
700 : :
6151 magnus@hagander.net 701 :CBC 1282 : file_type++;
702 : :
703 [ - + ]: 1282 : for (rm = resultmap; rm != NULL; rm = rm->next)
704 : : {
6151 magnus@hagander.net 705 [ # # # # ]:UBC 0 : if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
706 : : {
707 : 0 : return rm->resultfile;
708 : : }
709 : : }
710 : :
6151 magnus@hagander.net 711 :CBC 1282 : return NULL;
712 : : }
713 : :
714 : : /*
715 : : * Prepare environment variables for running regression tests
716 : : */
717 : : static void
6479 tgl@sss.pgh.pa.us 718 : 87 : initialize_environment(void)
719 : : {
720 : : /*
721 : : * Set default application_name. (The test_start_function may choose to
722 : : * override this, but if it doesn't, we have something useful in place.)
723 : : */
1201 724 : 87 : setenv("PGAPPNAME", "pg_regress", 1);
725 : :
726 : : /*
727 : : * Set variables that the test scripts may need to refer to.
728 : : */
846 729 : 87 : setenv("PG_ABS_SRCDIR", inputdir, 1);
730 : 87 : setenv("PG_ABS_BUILDDIR", outputdir, 1);
731 : 87 : setenv("PG_LIBDIR", dlpath, 1);
732 : 87 : setenv("PG_DLSUFFIX", DLSUFFIX, 1);
733 : :
5541 peter_e@gmx.net 734 [ + + ]: 87 : if (nolocale)
735 : : {
736 : : /*
737 : : * Clear out any non-C locale settings
738 : : */
739 : 2 : unsetenv("LC_COLLATE");
740 : 2 : unsetenv("LC_CTYPE");
741 : 2 : unsetenv("LC_MONETARY");
742 : 2 : unsetenv("LC_NUMERIC");
743 : 2 : unsetenv("LC_TIME");
744 : 2 : unsetenv("LANG");
745 : :
746 : : /*
747 : : * Most platforms have adopted the POSIX locale as their
748 : : * implementation-defined default locale. Exceptions include native
749 : : * Windows, macOS with --enable-nls, and Cygwin with --enable-nls.
750 : : * (Use of --enable-nls matters because libintl replaces setlocale().)
751 : : * Also, PostgreSQL does not support macOS with locale environment
752 : : * variables unset; see PostmasterMain().
753 : : */
754 : : #if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
755 : : setenv("LANG", "C", 1);
756 : : #endif
757 : : }
758 : :
759 : : /*
760 : : * Set translation-related settings to English; otherwise psql will
761 : : * produce translated messages and produce diffs. (XXX If we ever support
762 : : * translation of pg_regress, this needs to be moved elsewhere, where psql
763 : : * is actually called.)
764 : : */
5540 765 : 87 : unsetenv("LANGUAGE");
766 : 87 : unsetenv("LC_ALL");
1201 tgl@sss.pgh.pa.us 767 : 87 : setenv("LC_MESSAGES", "C", 1);
768 : :
769 : : /*
770 : : * Set encoding as requested
771 : : */
6479 772 [ + + ]: 87 : if (encoding)
1201 773 : 1 : setenv("PGCLIENTENCODING", encoding, 1);
774 : : else
6479 775 : 86 : unsetenv("PGCLIENTENCODING");
776 : :
777 : : /*
778 : : * Set timezone and datestyle for datetime-related tests
779 : : */
1201 780 : 87 : setenv("PGTZ", "PST8PDT", 1);
781 : 87 : setenv("PGDATESTYLE", "Postgres, MDY", 1);
782 : :
783 : : /*
784 : : * Likewise set intervalstyle to ensure consistent results. This is a bit
785 : : * more painful because we must use PGOPTIONS, and we want to preserve the
786 : : * user's ability to set other variables through that.
787 : : */
788 : : {
5618 789 : 87 : const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
5619 790 : 87 : const char *old_pgoptions = getenv("PGOPTIONS");
791 : : char *new_pgoptions;
792 : :
793 [ + - ]: 87 : if (!old_pgoptions)
794 : 87 : old_pgoptions = "";
1201 795 : 87 : new_pgoptions = psprintf("%s %s",
796 : : old_pgoptions, my_pgoptions);
797 : 87 : setenv("PGOPTIONS", new_pgoptions, 1);
798 : 87 : free(new_pgoptions);
799 : : }
800 : :
3279 peter_e@gmx.net 801 [ + + ]: 87 : if (temp_instance)
802 : : {
803 : : /*
804 : : * Clear out any environment vars that might cause psql to connect to
805 : : * the wrong postmaster, or otherwise behave in nondefault ways. (Note
806 : : * we also use psql's -X switch consistently, so that ~/.psqlrc files
807 : : * won't mess things up.) Also, set PGPORT to the temp port, and set
808 : : * PGHOST depending on whether we are using TCP or Unix sockets.
809 : : *
810 : : * This list should be kept in sync with PostgreSQL/Test/Utils.pm.
811 : : */
1036 michael@paquier.xyz 812 : 85 : unsetenv("PGCHANNELBINDING");
813 : : /* PGCLIENTENCODING, see above */
814 : 85 : unsetenv("PGCONNECT_TIMEOUT");
815 : 85 : unsetenv("PGDATA");
6479 tgl@sss.pgh.pa.us 816 : 85 : unsetenv("PGDATABASE");
328 817 : 85 : unsetenv("PGGSSDELEGATION");
1036 michael@paquier.xyz 818 : 85 : unsetenv("PGGSSENCMODE");
819 : 85 : unsetenv("PGGSSLIB");
820 : : /* PGHOSTADDR, see below */
821 : 85 : unsetenv("PGKRBSRVNAME");
822 : 85 : unsetenv("PGPASSFILE");
823 : 85 : unsetenv("PGPASSWORD");
824 : 85 : unsetenv("PGREQUIREPEER");
825 : 85 : unsetenv("PGREQUIRESSL");
6479 tgl@sss.pgh.pa.us 826 : 85 : unsetenv("PGSERVICE");
1036 michael@paquier.xyz 827 : 85 : unsetenv("PGSERVICEFILE");
828 : 85 : unsetenv("PGSSLCERT");
829 : 85 : unsetenv("PGSSLCRL");
830 : 85 : unsetenv("PGSSLCRLDIR");
831 : 85 : unsetenv("PGSSLKEY");
832 : 85 : unsetenv("PGSSLMAXPROTOCOLVERSION");
833 : 85 : unsetenv("PGSSLMINPROTOCOLVERSION");
6479 tgl@sss.pgh.pa.us 834 : 85 : unsetenv("PGSSLMODE");
1036 michael@paquier.xyz 835 : 85 : unsetenv("PGSSLROOTCERT");
836 : 85 : unsetenv("PGSSLSNI");
837 : 85 : unsetenv("PGTARGETSESSIONATTRS");
838 : 85 : unsetenv("PGUSER");
839 : : /* PGPORT, see below */
840 : : /* PGHOST, see below */
841 : :
6479 tgl@sss.pgh.pa.us 842 [ - + ]: 85 : if (hostname != NULL)
1201 tgl@sss.pgh.pa.us 843 :UBC 0 : setenv("PGHOST", hostname, 1);
844 : : else
845 : : {
3592 noah@leadboat.com 846 :CBC 85 : sockdir = getenv("PG_REGRESS_SOCK_DIR");
847 [ + - ]: 85 : if (!sockdir)
848 : 85 : sockdir = make_temp_sockdir();
1201 tgl@sss.pgh.pa.us 849 : 85 : setenv("PGHOST", sockdir, 1);
850 : : }
6479 851 : 85 : unsetenv("PGHOSTADDR");
852 [ + - ]: 85 : if (port != -1)
853 : : {
854 : : char s[16];
855 : :
172 dgustafsson@postgres 856 :GNC 85 : snprintf(s, sizeof(s), "%d", port);
1201 tgl@sss.pgh.pa.us 857 :CBC 85 : setenv("PGPORT", s, 1);
858 : : }
859 : : }
860 : : else
861 : : {
862 : : const char *pghost;
863 : : const char *pgport;
864 : :
865 : : /*
866 : : * When testing an existing install, we honor existing environment
867 : : * variables, except if they're overridden by command line options.
868 : : */
6479 869 [ + - ]: 2 : if (hostname != NULL)
870 : : {
1201 871 : 2 : setenv("PGHOST", hostname, 1);
6479 872 : 2 : unsetenv("PGHOSTADDR");
873 : : }
874 [ + - ]: 2 : if (port != -1)
875 : : {
876 : : char s[16];
877 : :
172 dgustafsson@postgres 878 :GNC 2 : snprintf(s, sizeof(s), "%d", port);
1201 tgl@sss.pgh.pa.us 879 :CBC 2 : setenv("PGPORT", s, 1);
880 : : }
6479 881 [ - + ]: 2 : if (user != NULL)
1201 tgl@sss.pgh.pa.us 882 :UBC 0 : setenv("PGUSER", user, 1);
883 : :
884 : : /*
885 : : * However, we *don't* honor PGDATABASE, since we certainly don't wish
886 : : * to connect to whatever database the user might like as default.
887 : : * (Most tests override PGDATABASE anyway, but there are some ECPG
888 : : * test cases that don't.)
889 : : */
1661 tgl@sss.pgh.pa.us 890 :CBC 2 : unsetenv("PGDATABASE");
891 : :
892 : : /*
893 : : * Report what we're connecting to
894 : : */
6479 895 : 2 : pghost = getenv("PGHOST");
896 : 2 : pgport = getenv("PGPORT");
897 [ - + ]: 2 : if (!pghost)
898 : : {
899 : : /* Keep this bit in sync with libpq's default host location: */
772 tgl@sss.pgh.pa.us 900 [ # # ]:UBC 0 : if (DEFAULT_PGSOCKET_DIR[0])
901 : : /* do nothing, we'll print "Unix socket" below */ ;
902 : : else
903 : 0 : pghost = "localhost"; /* DefaultHost in fe-connect.c */
904 : : }
905 : :
6479 tgl@sss.pgh.pa.us 906 [ + - + - ]:CBC 2 : if (pghost && pgport)
380 dgustafsson@postgres 907 : 2 : note("using postmaster on %s, port %s", pghost, pgport);
6479 tgl@sss.pgh.pa.us 908 [ + - - + ]: 2 : if (pghost && !pgport)
380 dgustafsson@postgres 909 :UBC 0 : note("using postmaster on %s, default port", pghost);
6479 tgl@sss.pgh.pa.us 910 [ - + - - ]:CBC 2 : if (!pghost && pgport)
380 dgustafsson@postgres 911 :UBC 0 : note("using postmaster on Unix socket, port %s", pgport);
6479 tgl@sss.pgh.pa.us 912 [ - + - - ]:CBC 2 : if (!pghost && !pgport)
380 dgustafsson@postgres 913 :UBC 0 : note("using postmaster on Unix socket, default port");
914 : : }
915 : :
6479 tgl@sss.pgh.pa.us 916 :CBC 87 : load_resultmap();
917 : 87 : }
918 : :
919 : : #ifdef ENABLE_SSPI
920 : :
921 : : /* support for config_sspi_auth() */
922 : : static const char *
923 : : fmtHba(const char *raw)
924 : : {
925 : : static char *ret;
926 : : const char *rp;
927 : : char *wp;
928 : :
929 : : wp = ret = pg_realloc(ret, 3 + strlen(raw) * 2);
930 : :
931 : : *wp++ = '"';
932 : : for (rp = raw; *rp; rp++)
933 : : {
934 : : if (*rp == '"')
935 : : *wp++ = '"';
936 : : *wp++ = *rp;
937 : : }
938 : : *wp++ = '"';
939 : : *wp++ = '\0';
940 : :
941 : : return ret;
942 : : }
943 : :
944 : : /*
945 : : * Get account and domain/realm names for the current user. This is based on
946 : : * pg_SSPI_recvauth(). The returned strings use static storage.
947 : : */
948 : : static void
949 : : current_windows_user(const char **acct, const char **dom)
950 : : {
951 : : static char accountname[MAXPGPATH];
952 : : static char domainname[MAXPGPATH];
953 : : HANDLE token;
954 : : TOKEN_USER *tokenuser;
955 : : DWORD retlen;
956 : : DWORD accountnamesize = sizeof(accountname);
957 : : DWORD domainnamesize = sizeof(domainname);
958 : : SID_NAME_USE accountnameuse;
959 : :
960 : : if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
961 : : {
962 : : bail("could not open process token: error code %lu", GetLastError());
963 : : }
964 : :
965 : : if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
966 : : {
967 : : bail("could not get token information buffer size: error code %lu",
968 : : GetLastError());
969 : : }
970 : : tokenuser = pg_malloc(retlen);
971 : : if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
972 : : {
973 : : bail("could not get token information: error code %lu",
974 : : GetLastError());
975 : : }
976 : :
977 : : if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
978 : : domainname, &domainnamesize, &accountnameuse))
979 : : {
980 : : bail("could not look up account SID: error code %lu",
981 : : GetLastError());
982 : : }
983 : :
984 : : free(tokenuser);
985 : :
986 : : *acct = accountname;
987 : : *dom = domainname;
988 : : }
989 : :
990 : : /*
991 : : * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
992 : : * the current OS user to authenticate as the bootstrap superuser and as any
993 : : * user named in a --create-role option.
994 : : *
995 : : * In --config-auth mode, the --user switch can be used to specify the
996 : : * bootstrap superuser's name, otherwise we assume it is the default.
997 : : */
998 : : static void
999 : : config_sspi_auth(const char *pgdata, const char *superuser_name)
1000 : : {
1001 : : const char *accountname,
1002 : : *domainname;
1003 : : char *errstr;
1004 : : bool have_ipv6;
1005 : : char fname[MAXPGPATH];
1006 : : int res;
1007 : : FILE *hba,
1008 : : *ident;
1009 : : _stringlist *sl;
1010 : :
1011 : : /* Find out the name of the current OS user */
1012 : : current_windows_user(&accountname, &domainname);
1013 : :
1014 : : /* Determine the bootstrap superuser's name */
1015 : : if (superuser_name == NULL)
1016 : : {
1017 : : /*
1018 : : * Compute the default superuser name the same way initdb does.
1019 : : *
1020 : : * It's possible that this result always matches "accountname", the
1021 : : * value SSPI authentication discovers. But the underlying system
1022 : : * functions do not clearly guarantee that.
1023 : : */
1024 : : superuser_name = get_user_name(&errstr);
1025 : : if (superuser_name == NULL)
1026 : : {
1027 : : bail("%s", errstr);
1028 : : }
1029 : : }
1030 : :
1031 : : /*
1032 : : * Like initdb.c:setup_config(), determine whether the platform recognizes
1033 : : * ::1 (IPv6 loopback) as a numeric host address string.
1034 : : */
1035 : : {
1036 : : struct addrinfo *gai_result;
1037 : : struct addrinfo hints;
1038 : : WSADATA wsaData;
1039 : :
1040 : : hints.ai_flags = AI_NUMERICHOST;
1041 : : hints.ai_family = AF_UNSPEC;
1042 : : hints.ai_socktype = 0;
1043 : : hints.ai_protocol = 0;
1044 : : hints.ai_addrlen = 0;
1045 : : hints.ai_canonname = NULL;
1046 : : hints.ai_addr = NULL;
1047 : : hints.ai_next = NULL;
1048 : :
1049 : : have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 &&
1050 : : getaddrinfo("::1", NULL, &hints, &gai_result) == 0);
1051 : : }
1052 : :
1053 : : /* Check a Write outcome and report any error. */
1054 : : #define CW(cond) \
1055 : : do { \
1056 : : if (!(cond)) \
1057 : : bail("could not write to file \"%s\": %m", fname); \
1058 : : } while (0)
1059 : :
1060 : : res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
1061 : : if (res < 0 || res >= sizeof(fname))
1062 : : {
1063 : : /*
1064 : : * Truncating this name is a fatal error, because we must not fail to
1065 : : * overwrite an original trust-authentication pg_hba.conf.
1066 : : */
1067 : : bail("directory name too long");
1068 : : }
1069 : : hba = fopen(fname, "w");
1070 : : if (hba == NULL)
1071 : : {
1072 : : bail("could not open file \"%s\" for writing: %m", fname);
1073 : : }
1074 : : CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
1075 : : CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
1076 : : hba) >= 0);
1077 : : if (have_ipv6)
1078 : : CW(fputs("host all all ::1/128 sspi include_realm=1 map=regress\n",
1079 : : hba) >= 0);
1080 : : CW(fclose(hba) == 0);
1081 : :
1082 : : snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
1083 : : ident = fopen(fname, "w");
1084 : : if (ident == NULL)
1085 : : {
1086 : : bail("could not open file \"%s\" for writing: %m", fname);
1087 : : }
1088 : : CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
1089 : :
1090 : : /*
1091 : : * Double-quote for the benefit of account names containing whitespace or
1092 : : * '#'. Windows forbids the double-quote character itself, so don't
1093 : : * bother escaping embedded double-quote characters.
1094 : : */
1095 : : CW(fprintf(ident, "regress \"%s@%s\" %s\n",
1096 : : accountname, domainname, fmtHba(superuser_name)) >= 0);
1097 : : for (sl = extraroles; sl; sl = sl->next)
1098 : : CW(fprintf(ident, "regress \"%s@%s\" %s\n",
1099 : : accountname, domainname, fmtHba(sl->str)) >= 0);
1100 : : CW(fclose(ident) == 0);
1101 : : }
1102 : :
1103 : : #endif /* ENABLE_SSPI */
1104 : :
1105 : : /*
1106 : : * psql_start_command, psql_add_command, psql_end_command
1107 : : *
1108 : : * Issue one or more commands within one psql call.
1109 : : * Set up with psql_start_command, then add commands one at a time
1110 : : * with psql_add_command, and finally execute with psql_end_command.
1111 : : *
1112 : : * Since we use system(), this doesn't return until the operation finishes
1113 : : */
1114 : : static StringInfo
907 1115 : 97 : psql_start_command(void)
1116 : : {
1117 : 97 : StringInfo buf = makeStringInfo();
1118 : :
907 tgl@sss.pgh.pa.us 1119 :UBC 0 : appendStringInfo(buf,
1120 : : "\"%s%spsql\" -X -q",
907 tgl@sss.pgh.pa.us 1121 [ - + ]:CBC 97 : bindir ? bindir : "",
1122 [ - + ]: 97 : bindir ? "/" : "");
1123 : 97 : return buf;
1124 : : }
1125 : :
1126 : : static void
1127 : 191 : psql_add_command(StringInfo buf, const char *query,...)
1128 : : {
1129 : : StringInfoData cmdbuf;
1130 : : const char *cmdptr;
1131 : :
1132 : : /* Add each command as a -c argument in the psql call */
1133 : 191 : appendStringInfoString(buf, " -c \"");
1134 : :
1135 : : /* Generate the query with insertion of sprintf arguments */
1136 : 191 : initStringInfo(&cmdbuf);
1137 : : for (;;)
907 tgl@sss.pgh.pa.us 1138 :UBC 0 : {
1139 : : va_list args;
1140 : : int needed;
1141 : :
907 tgl@sss.pgh.pa.us 1142 :CBC 191 : va_start(args, query);
1143 : 191 : needed = appendStringInfoVA(&cmdbuf, query, args);
1144 : 191 : va_end(args);
1145 [ + - ]: 191 : if (needed == 0)
1146 : 191 : break; /* success */
907 tgl@sss.pgh.pa.us 1147 :UBC 0 : enlargeStringInfo(&cmdbuf, needed);
1148 : : }
1149 : :
1150 : : /* Now escape any shell double-quote metacharacters */
907 tgl@sss.pgh.pa.us 1151 [ + + ]:CBC 40504 : for (cmdptr = cmdbuf.data; *cmdptr; cmdptr++)
1152 : : {
1153 [ + + ]: 40313 : if (strchr("\\\"$`", *cmdptr))
1154 : 1266 : appendStringInfoChar(buf, '\\');
1155 : 40313 : appendStringInfoChar(buf, *cmdptr);
1156 : : }
1157 : :
1158 : 191 : appendStringInfoChar(buf, '"');
1159 : :
1160 : 191 : pfree(cmdbuf.data);
1161 : 191 : }
1162 : :
1163 : : static void
1164 : 97 : psql_end_command(StringInfo buf, const char *database)
1165 : : {
1166 : : /* Add the database name --- assume it needs no extra escaping */
1167 : 97 : appendStringInfo(buf,
1168 : : " \"%s\"",
1169 : : database);
1170 : :
1171 : : /* And now we can execute the shell command */
594 1172 : 97 : fflush(NULL);
907 1173 [ - + ]: 97 : if (system(buf->data) != 0)
1174 : : {
1175 : : /* psql probably already reported the error */
380 dgustafsson@postgres 1176 :UBC 0 : bail("command failed: %s", buf->data);
1177 : : }
1178 : :
1179 : : /* Clean up */
29 dgustafsson@postgres 1180 :GNC 97 : destroyStringInfo(buf);
6479 tgl@sss.pgh.pa.us 1181 :CBC 97 : }
1182 : :
1183 : : /*
1184 : : * Shorthand macro for the common case of a single command
1185 : : */
1186 : : #define psql_command(database, ...) \
1187 : : do { \
1188 : : StringInfo cmdbuf = psql_start_command(); \
1189 : : psql_add_command(cmdbuf, __VA_ARGS__); \
1190 : : psql_end_command(cmdbuf, database); \
1191 : : } while (0)
1192 : :
1193 : : /*
1194 : : * Spawn a process to execute the given shell command; don't wait for it
1195 : : *
1196 : : * Returns the process ID (or HANDLE) so we can wait for it later
1197 : : */
1198 : : PID_TYPE
1199 : 1237 : spawn_process(const char *cmdline)
1200 : : {
1201 : : #ifndef WIN32
1202 : : pid_t pid;
1203 : :
1204 : : /*
1205 : : * Must flush I/O buffers before fork.
1206 : : */
594 1207 : 1237 : fflush(NULL);
1208 : :
1209 : : #ifdef EXEC_BACKEND
1210 : : pg_disable_aslr();
1211 : : #endif
1212 : :
6479 1213 : 1237 : pid = fork();
1214 [ - + ]: 2474 : if (pid == -1)
1215 : : {
10 michael@paquier.xyz 1216 :UNC 0 : bail("could not fork: %m");
1217 : : }
6479 tgl@sss.pgh.pa.us 1218 [ + + ]:CBC 2474 : if (pid == 0)
1219 : : {
1220 : : /*
1221 : : * In child
1222 : : *
1223 : : * Instead of using system(), exec the shell directly, and tell it to
1224 : : * "exec" the command too. This saves two useless processes per
1225 : : * parallel test case.
1226 : : */
1227 : : char *cmdline2;
1228 : :
3827 1229 : 1237 : cmdline2 = psprintf("exec %s", cmdline);
6115 alvherre@alvh.no-ip. 1230 : 1237 : execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
1231 : : /* Not using the normal bail() here as we want _exit */
10 michael@paquier.xyz 1232 :GNC 1237 : bail_noatexit("could not exec \"%s\": %m", shellprog);
1233 : : }
1234 : : /* in parent */
6479 tgl@sss.pgh.pa.us 1235 :CBC 1237 : return pid;
1236 : : #else
1237 : : PROCESS_INFORMATION pi;
1238 : : char *cmdline2;
1239 : : const char *comspec;
1240 : :
1241 : : /* Find CMD.EXE location using COMSPEC, if it's set */
1242 : : comspec = getenv("COMSPEC");
1243 : : if (comspec == NULL)
1244 : : comspec = "CMD";
1245 : :
1246 : : memset(&pi, 0, sizeof(pi));
1247 : : cmdline2 = psprintf("\"%s\" /d /c \"%s\"", comspec, cmdline);
1248 : :
1249 : : if (!CreateRestrictedProcess(cmdline2, &pi))
1250 : : exit(2);
1251 : :
1252 : : CloseHandle(pi.hThread);
1253 : : return pi.hProcess;
1254 : : #endif
1255 : : }
1256 : :
1257 : : /*
1258 : : * Count bytes in file
1259 : : */
1260 : : static long
1261 : 87 : file_size(const char *file)
1262 : : {
1263 : : long r;
6402 bruce@momjian.us 1264 : 87 : FILE *f = fopen(file, "r");
1265 : :
6479 tgl@sss.pgh.pa.us 1266 [ - + ]: 87 : if (!f)
1267 : : {
10 michael@paquier.xyz 1268 :UNC 0 : diag("could not open file \"%s\" for reading: %m", file);
6479 tgl@sss.pgh.pa.us 1269 :UBC 0 : return -1;
1270 : : }
6479 tgl@sss.pgh.pa.us 1271 :CBC 87 : fseek(f, 0, SEEK_END);
1272 : 87 : r = ftell(f);
1273 : 87 : fclose(f);
1274 : 87 : return r;
1275 : : }
1276 : :
1277 : : /*
1278 : : * Count lines in file
1279 : : */
1280 : : static int
1281 : 31 : file_line_count(const char *file)
1282 : : {
1283 : : int c;
6402 bruce@momjian.us 1284 : 31 : int l = 0;
1285 : 31 : FILE *f = fopen(file, "r");
1286 : :
6479 tgl@sss.pgh.pa.us 1287 [ - + ]: 31 : if (!f)
1288 : : {
10 michael@paquier.xyz 1289 :UNC 0 : diag("could not open file \"%s\" for reading: %m", file);
6479 tgl@sss.pgh.pa.us 1290 :UBC 0 : return -1;
1291 : : }
6479 tgl@sss.pgh.pa.us 1292 [ + + ]:CBC 199388 : while ((c = fgetc(f)) != EOF)
1293 : : {
1294 [ + + ]: 199357 : if (c == '\n')
1295 : 7286 : l++;
1296 : : }
1297 : 31 : fclose(f);
1298 : 31 : return l;
1299 : : }
1300 : :
1301 : : bool
1302 : 2239 : file_exists(const char *file)
1303 : : {
6402 bruce@momjian.us 1304 : 2239 : FILE *f = fopen(file, "r");
1305 : :
6479 tgl@sss.pgh.pa.us 1306 [ + + ]: 2239 : if (!f)
1307 : 1699 : return false;
1308 : 540 : fclose(f);
1309 : 540 : return true;
1310 : : }
1311 : :
1312 : : static bool
1313 : 344 : directory_exists(const char *dir)
1314 : : {
1315 : : struct stat st;
1316 : :
1317 [ + + ]: 344 : if (stat(dir, &st) != 0)
1318 : 257 : return false;
5858 1319 [ + - ]: 87 : if (S_ISDIR(st.st_mode))
6479 1320 : 87 : return true;
6479 tgl@sss.pgh.pa.us 1321 :UBC 0 : return false;
1322 : : }
1323 : :
1324 : : /* Create a directory */
1325 : : static void
6479 tgl@sss.pgh.pa.us 1326 :CBC 257 : make_directory(const char *dir)
1327 : : {
6471 1328 [ - + ]: 257 : if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
10 michael@paquier.xyz 1329 :UNC 0 : bail("could not create directory \"%s\": %m", dir);
6479 tgl@sss.pgh.pa.us 1330 :CBC 257 : }
1331 : :
1332 : : /*
1333 : : * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
1334 : : */
1335 : : static char *
6151 magnus@hagander.net 1336 : 65 : get_alternative_expectfile(const char *expectfile, int i)
1337 : : {
1338 : : char *last_dot;
5995 bruce@momjian.us 1339 : 65 : int ssize = strlen(expectfile) + 2 + 1;
1340 : : char *tmp;
1341 : : char *s;
1342 : :
3631 1343 [ - + ]: 65 : if (!(tmp = (char *) malloc(ssize)))
3697 sfrost@snowman.net 1344 :UBC 0 : return NULL;
1345 : :
3631 bruce@momjian.us 1346 [ - + ]:CBC 65 : if (!(s = (char *) malloc(ssize)))
1347 : : {
3695 sfrost@snowman.net 1348 :UBC 0 : free(tmp);
1349 : 0 : return NULL;
1350 : : }
1351 : :
6151 magnus@hagander.net 1352 :CBC 65 : strcpy(tmp, expectfile);
5995 bruce@momjian.us 1353 : 65 : last_dot = strrchr(tmp, '.');
6151 magnus@hagander.net 1354 [ - + ]: 65 : if (!last_dot)
1355 : : {
5575 bruce@momjian.us 1356 :UBC 0 : free(tmp);
1357 : 0 : free(s);
6151 magnus@hagander.net 1358 : 0 : return NULL;
1359 : : }
6151 magnus@hagander.net 1360 :CBC 65 : *last_dot = '\0';
5995 bruce@momjian.us 1361 : 65 : snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
6151 magnus@hagander.net 1362 : 65 : free(tmp);
1363 : 65 : return s;
1364 : : }
1365 : :
1366 : : /*
1367 : : * Run a "diff" command and also check that it didn't crash
1368 : : */
1369 : : static int
6468 bruce@momjian.us 1370 : 1313 : run_diff(const char *cmd, const char *filename)
1371 : : {
1372 : : int r;
1373 : :
594 tgl@sss.pgh.pa.us 1374 : 1313 : fflush(NULL);
6478 1375 : 1313 : r = system(cmd);
1376 [ + - - + ]: 1313 : if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
1377 : : {
380 dgustafsson@postgres 1378 :UBC 0 : bail("diff command failed with status %d: %s", r, cmd);
1379 : : }
1380 : : #ifdef WIN32
1381 : :
1382 : : /*
1383 : : * On WIN32, if the 'diff' command cannot be found, system() returns 1,
1384 : : * but produces nothing to stdout, so we check for that here.
1385 : : */
1386 : : if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
1387 : : {
1388 : : bail("diff command not found: %s", cmd);
1389 : : }
1390 : : #endif
1391 : :
6468 bruce@momjian.us 1392 :CBC 1313 : return WEXITSTATUS(r);
1393 : : }
1394 : :
1395 : : /*
1396 : : * Check the actual result file for the given test against expected results
1397 : : *
1398 : : * Returns true if different (failure), false if correct match found.
1399 : : * In the true case, the diff is appended to the diffs file.
1400 : : */
1401 : : static bool
6151 magnus@hagander.net 1402 : 1282 : results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
1403 : : {
1404 : : char expectfile[MAXPGPATH];
1405 : : char diff[MAXPGPATH];
1406 : : char cmd[MAXPGPATH * 3];
1407 : : char best_expect_file[MAXPGPATH];
1408 : : FILE *difffile;
1409 : : int best_line_count;
1410 : : int i;
1411 : : int l;
1412 : : const char *platform_expectfile;
1413 : :
1414 : : /*
1415 : : * We can pass either the resultsfile or the expectfile, they should have
1416 : : * the same type (filename.type) anyway.
1417 : : */
1418 : 1282 : platform_expectfile = get_expectfile(testname, resultsfile);
1419 : :
3709 tgl@sss.pgh.pa.us 1420 : 1282 : strlcpy(expectfile, default_expectfile, sizeof(expectfile));
5995 bruce@momjian.us 1421 [ - + ]: 1282 : if (platform_expectfile)
1422 : : {
1423 : : /*
1424 : : * Replace everything after the last slash in expectfile with what the
1425 : : * platform_expectfile contains.
1426 : : */
5995 bruce@momjian.us 1427 :UBC 0 : char *p = strrchr(expectfile, '/');
1428 : :
6151 magnus@hagander.net 1429 [ # # ]: 0 : if (p)
1430 : 0 : strcpy(++p, platform_expectfile);
1431 : : }
1432 : :
1433 : : /* Name to use for temporary diff file */
6151 magnus@hagander.net 1434 :CBC 1282 : snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
1435 : :
1436 : : /* OK, run the diff */
6479 tgl@sss.pgh.pa.us 1437 : 1282 : snprintf(cmd, sizeof(cmd),
1438 : : "diff %s \"%s\" \"%s\" > \"%s\"",
1439 : : basic_diff_opts, expectfile, resultsfile, diff);
1440 : :
1441 : : /* Is the diff file empty? */
6468 bruce@momjian.us 1442 [ + + ]: 1282 : if (run_diff(cmd, diff) == 0)
1443 : : {
6479 tgl@sss.pgh.pa.us 1444 : 1255 : unlink(diff);
1445 : 1255 : return false;
1446 : : }
1447 : :
1448 : : /* There may be secondary comparison files that match better */
1449 : 27 : best_line_count = file_line_count(diff);
1450 : 27 : strcpy(best_expect_file, expectfile);
1451 : :
1452 [ + - ]: 65 : for (i = 0; i <= 9; i++)
1453 : : {
1454 : : char *alt_expectfile;
1455 : :
6151 magnus@hagander.net 1456 : 65 : alt_expectfile = get_alternative_expectfile(expectfile, i);
3697 sfrost@snowman.net 1457 [ - + ]: 65 : if (!alt_expectfile)
10 michael@paquier.xyz 1458 :UNC 0 : bail("Unable to check secondary comparison files: %m");
1459 : :
6151 magnus@hagander.net 1460 [ + + ]:CBC 65 : if (!file_exists(alt_expectfile))
1461 : : {
3697 sfrost@snowman.net 1462 : 34 : free(alt_expectfile);
6479 tgl@sss.pgh.pa.us 1463 : 34 : continue;
1464 : : }
1465 : :
1466 : 31 : snprintf(cmd, sizeof(cmd),
1467 : : "diff %s \"%s\" \"%s\" > \"%s\"",
1468 : : basic_diff_opts, alt_expectfile, resultsfile, diff);
1469 : :
6468 bruce@momjian.us 1470 [ + + ]: 31 : if (run_diff(cmd, diff) == 0)
1471 : : {
6479 tgl@sss.pgh.pa.us 1472 : 27 : unlink(diff);
3697 sfrost@snowman.net 1473 : 27 : free(alt_expectfile);
6479 tgl@sss.pgh.pa.us 1474 : 27 : return false;
1475 : : }
1476 : :
1477 : 4 : l = file_line_count(diff);
1478 [ + - ]: 4 : if (l < best_line_count)
1479 : : {
1480 : : /* This diff was a better match than the last one */
1481 : 4 : best_line_count = l;
3709 1482 : 4 : strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file));
1483 : : }
6151 magnus@hagander.net 1484 : 4 : free(alt_expectfile);
1485 : : }
1486 : :
1487 : : /*
1488 : : * fall back on the canonical results file if we haven't tried it yet and
1489 : : * haven't found a complete match yet.
1490 : : */
1491 : :
6151 magnus@hagander.net 1492 [ # # ]:UBC 0 : if (platform_expectfile)
1493 : : {
6466 andrew@dunslane.net 1494 : 0 : snprintf(cmd, sizeof(cmd),
1495 : : "diff %s \"%s\" \"%s\" > \"%s\"",
1496 : : basic_diff_opts, default_expectfile, resultsfile, diff);
1497 : :
1498 [ # # ]: 0 : if (run_diff(cmd, diff) == 0)
1499 : : {
1500 : : /* No diff = no changes = good */
1501 : 0 : unlink(diff);
1502 : 0 : return false;
1503 : : }
1504 : :
1505 : 0 : l = file_line_count(diff);
1506 [ # # ]: 0 : if (l < best_line_count)
1507 : : {
1508 : : /* This diff was a better match than the last one */
1509 : 0 : best_line_count = l;
3709 tgl@sss.pgh.pa.us 1510 : 0 : strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file));
1511 : : }
1512 : : }
1513 : :
1514 : : /*
1515 : : * Use the best comparison file to generate the "pretty" diff, which we
1516 : : * append to the diffs summary file.
1517 : : */
1518 : :
1519 : : /* Write diff header */
6479 1520 : 0 : difffile = fopen(difffilename, "a");
1521 [ # # ]: 0 : if (difffile)
1522 : : {
1523 : 0 : fprintf(difffile,
1524 : : "diff %s %s %s\n",
1525 : : pretty_diff_opts, best_expect_file, resultsfile);
1526 : 0 : fclose(difffile);
1527 : : }
1528 : :
1529 : : /* Run diff */
1929 peter@eisentraut.org 1530 : 0 : snprintf(cmd, sizeof(cmd),
1531 : : "diff %s \"%s\" \"%s\" >> \"%s\"",
1532 : : pretty_diff_opts, best_expect_file, resultsfile, difffilename);
1533 : 0 : run_diff(cmd, difffilename);
1534 : :
6479 tgl@sss.pgh.pa.us 1535 : 0 : unlink(diff);
1536 : 0 : return true;
1537 : : }
1538 : :
1539 : : /*
1540 : : * Wait for specified subprocesses to finish, and return their exit
1541 : : * statuses into statuses[] and stop times into stoptimes[]
1542 : : *
1543 : : * If names isn't NULL, print each subprocess's name as it finishes
1544 : : *
1545 : : * Note: it's OK to scribble on the pids array, but not on the names array
1546 : : */
1547 : : static void
1890 tgl@sss.pgh.pa.us 1548 :CBC 561 : wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
1549 : : char **names, int num_tests)
1550 : : {
1551 : : int tests_left;
1552 : : int i;
1553 : :
1554 : : #ifdef WIN32
1555 : : PID_TYPE *active_pids = pg_malloc(num_tests * sizeof(PID_TYPE));
1556 : :
1557 : : memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
1558 : : #endif
1559 : :
6479 1560 : 561 : tests_left = num_tests;
1561 [ + + ]: 1713 : while (tests_left > 0)
1562 : : {
1563 : : PID_TYPE p;
1564 : :
1565 : : #ifndef WIN32
1566 : : int exit_status;
1567 : :
5811 1568 : 1152 : p = wait(&exit_status);
1569 : :
6478 1570 [ - + ]: 1152 : if (p == INVALID_PID)
10 michael@paquier.xyz 1571 :UNC 0 : bail("failed to wait for subprocesses: %m");
1572 : : #else
1573 : : DWORD exit_status;
1574 : : int r;
1575 : :
1576 : : r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
1577 : : if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
1578 : : {
1579 : : bail("failed to wait for subprocesses: error code %lu",
1580 : : GetLastError());
1581 : : }
1582 : : p = active_pids[r - WAIT_OBJECT_0];
1583 : : /* compact the active_pids array */
1584 : : active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
1585 : : #endif /* WIN32 */
1586 : :
6402 bruce@momjian.us 1587 [ + - ]:CBC 5934 : for (i = 0; i < num_tests; i++)
1588 : : {
6479 tgl@sss.pgh.pa.us 1589 [ + + ]: 5934 : if (p == pids[i])
1590 : : {
1591 : : #ifdef WIN32
1592 : : GetExitCodeProcess(pids[i], &exit_status);
1593 : : CloseHandle(pids[i]);
1594 : : #endif
6478 1595 : 1152 : pids[i] = INVALID_PID;
5555 magnus@hagander.net 1596 : 1152 : statuses[i] = (int) exit_status;
1890 tgl@sss.pgh.pa.us 1597 : 1152 : INSTR_TIME_SET_CURRENT(stoptimes[i]);
6478 1598 [ + + ]: 1152 : if (names)
380 dgustafsson@postgres 1599 : 645 : note_detail(" %s", names[i]);
6479 tgl@sss.pgh.pa.us 1600 : 1152 : tests_left--;
6478 1601 : 1152 : break;
1602 : : }
1603 : : }
1604 : : }
1605 : :
1606 : : #ifdef WIN32
1607 : : free(active_pids);
1608 : : #endif
6479 1609 : 561 : }
1610 : :
1611 : : /*
1612 : : * report nonzero exit code from a test process
1613 : : */
1614 : : static void
5811 tgl@sss.pgh.pa.us 1615 :UBC 0 : log_child_failure(int exitstatus)
1616 : : {
1617 [ # # ]: 0 : if (WIFEXITED(exitstatus))
380 dgustafsson@postgres 1618 : 0 : diag("(test process exited with exit code %d)",
1619 : : WEXITSTATUS(exitstatus));
5811 tgl@sss.pgh.pa.us 1620 [ # # ]: 0 : else if (WIFSIGNALED(exitstatus))
1621 : : {
1622 : : #if defined(WIN32)
1623 : : diag("(test process was terminated by exception 0x%X)",
1624 : : WTERMSIG(exitstatus));
1625 : : #else
380 dgustafsson@postgres 1626 : 0 : diag("(test process was terminated by signal %d: %s)",
1627 : : WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
1628 : : #endif
1629 : : }
1630 : : else
1631 : 0 : diag("(test process exited with unrecognized status %d)", exitstatus);
5811 tgl@sss.pgh.pa.us 1632 : 0 : }
1633 : :
1634 : : /*
1635 : : * Run all the tests specified in one schedule file
1636 : : */
1637 : : static void
1189 tgl@sss.pgh.pa.us 1638 :CBC 5 : run_schedule(const char *schedule, test_start_function startfunc,
1639 : : postprocess_result_function postfunc)
1640 : : {
1641 : : #define MAX_PARALLEL_TESTS 100
1642 : : char *tests[MAX_PARALLEL_TESTS];
1643 : : _stringlist *resultfiles[MAX_PARALLEL_TESTS];
1644 : : _stringlist *expectfiles[MAX_PARALLEL_TESTS];
1645 : : _stringlist *tags[MAX_PARALLEL_TESTS];
1646 : : PID_TYPE pids[MAX_PARALLEL_TESTS];
1647 : : instr_time starttimes[MAX_PARALLEL_TESTS];
1648 : : instr_time stoptimes[MAX_PARALLEL_TESTS];
1649 : : int statuses[MAX_PARALLEL_TESTS];
1650 : : char scbuf[1024];
1651 : : FILE *scf;
6402 bruce@momjian.us 1652 : 5 : int line_num = 0;
1653 : :
2381 tgl@sss.pgh.pa.us 1654 : 5 : memset(tests, 0, sizeof(tests));
1655 : 5 : memset(resultfiles, 0, sizeof(resultfiles));
1656 : 5 : memset(expectfiles, 0, sizeof(expectfiles));
1657 : 5 : memset(tags, 0, sizeof(tags));
1658 : :
6479 1659 : 5 : scf = fopen(schedule, "r");
1660 [ - + ]: 5 : if (!scf)
10 michael@paquier.xyz 1661 :UNC 0 : bail("could not open file \"%s\" for reading: %m", schedule);
1662 : :
6479 tgl@sss.pgh.pa.us 1663 [ + + ]:CBC 599 : while (fgets(scbuf, sizeof(scbuf), scf))
1664 : : {
6402 bruce@momjian.us 1665 : 594 : char *test = NULL;
1666 : : char *c;
1667 : : int num_tests;
1668 : : bool inword;
1669 : : int i;
1670 : :
6479 tgl@sss.pgh.pa.us 1671 : 594 : line_num++;
1672 : :
1673 : : /* strip trailing whitespace, especially the newline */
1674 : 594 : i = strlen(scbuf);
6402 bruce@momjian.us 1675 [ + + + + ]: 1188 : while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
6479 tgl@sss.pgh.pa.us 1676 : 594 : scbuf[--i] = '\0';
1677 : :
1678 [ + + + + ]: 594 : if (scbuf[0] == '\0' || scbuf[0] == '#')
1679 : 336 : continue;
1680 [ + - ]: 258 : if (strncmp(scbuf, "test: ", 6) == 0)
1681 : 258 : test = scbuf + 6;
1682 : : else
1683 : : {
380 dgustafsson@postgres 1684 :UBC 0 : bail("syntax error in schedule file \"%s\" line %d: %s",
1685 : : schedule, line_num, scbuf);
1686 : : }
1687 : :
6479 tgl@sss.pgh.pa.us 1688 :CBC 258 : num_tests = 0;
1689 : 258 : inword = false;
2381 1690 : 10142 : for (c = test;; c++)
1691 : : {
1692 [ + + + + ]: 10142 : if (*c == '\0' || isspace((unsigned char) *c))
1693 : : {
1694 [ + - ]: 849 : if (inword)
1695 : : {
1696 : : /* Reached end of a test name */
1697 : : char sav;
1698 : :
1699 [ - + ]: 849 : if (num_tests >= MAX_PARALLEL_TESTS)
1700 : : {
380 dgustafsson@postgres 1701 :UBC 0 : bail("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s",
1702 : : MAX_PARALLEL_TESTS, schedule, line_num, scbuf);
1703 : : }
2381 tgl@sss.pgh.pa.us 1704 :CBC 849 : sav = *c;
1705 : 849 : *c = '\0';
1706 : 849 : tests[num_tests] = pg_strdup(test);
1707 : 849 : num_tests++;
1708 : 849 : *c = sav;
1709 : 849 : inword = false;
1710 : : }
1711 [ + + ]: 849 : if (*c == '\0')
1712 : 258 : break; /* loop exit is here */
1713 : : }
6479 1714 [ + + ]: 9293 : else if (!inword)
1715 : : {
1716 : : /* Start of a test name */
2381 1717 : 849 : test = c;
6479 1718 : 849 : inword = true;
1719 : : }
1720 : : }
1721 : :
1722 [ - + ]: 258 : if (num_tests == 0)
1723 : : {
380 dgustafsson@postgres 1724 :UBC 0 : bail("syntax error in schedule file \"%s\" line %d: %s",
1725 : : schedule, line_num, scbuf);
1726 : : }
1727 : :
6479 tgl@sss.pgh.pa.us 1728 [ + + ]:CBC 258 : if (num_tests == 1)
1729 : : {
1189 1730 : 204 : pids[0] = (startfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
1890 1731 : 204 : INSTR_TIME_SET_CURRENT(starttimes[0]);
1732 : 204 : wait_for_tests(pids, statuses, stoptimes, NULL, 1);
1733 : : /* status line is finished below */
1734 : : }
2381 1735 [ + - - + ]: 54 : else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests)
1736 : : {
380 dgustafsson@postgres 1737 :UBC 0 : bail("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s",
1738 : : max_concurrent_tests, schedule, line_num, scbuf);
1739 : : }
6479 tgl@sss.pgh.pa.us 1740 [ - + - - ]:CBC 54 : else if (max_connections > 0 && max_connections < num_tests)
6479 tgl@sss.pgh.pa.us 1741 :UBC 0 : {
6402 bruce@momjian.us 1742 : 0 : int oldest = 0;
1743 : :
380 dgustafsson@postgres 1744 : 0 : note_detail("parallel group (%d tests, in groups of %d): ",
1745 : : num_tests, max_connections);
6479 tgl@sss.pgh.pa.us 1746 [ # # ]: 0 : for (i = 0; i < num_tests; i++)
1747 : : {
1748 [ # # ]: 0 : if (i - oldest >= max_connections)
1749 : : {
5811 1750 : 0 : wait_for_tests(pids + oldest, statuses + oldest,
1890 1751 : 0 : stoptimes + oldest,
5811 1752 : 0 : tests + oldest, i - oldest);
6479 1753 : 0 : oldest = i;
1754 : : }
1189 1755 : 0 : pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
1890 1756 : 0 : INSTR_TIME_SET_CURRENT(starttimes[i]);
1757 : : }
5811 1758 : 0 : wait_for_tests(pids + oldest, statuses + oldest,
1890 1759 : 0 : stoptimes + oldest,
5811 1760 : 0 : tests + oldest, i - oldest);
380 dgustafsson@postgres 1761 : 0 : note_end();
1762 : : }
1763 : : else
1764 : : {
380 dgustafsson@postgres 1765 :CBC 54 : note_detail("parallel group (%d tests): ", num_tests);
6479 tgl@sss.pgh.pa.us 1766 [ + + ]: 699 : for (i = 0; i < num_tests; i++)
1767 : : {
1189 1768 : 645 : pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
1890 1769 : 645 : INSTR_TIME_SET_CURRENT(starttimes[i]);
1770 : : }
1771 : 54 : wait_for_tests(pids, statuses, stoptimes, tests, num_tests);
380 dgustafsson@postgres 1772 : 54 : note_end();
1773 : : }
1774 : :
1775 : : /* Check results for all tests */
6479 tgl@sss.pgh.pa.us 1776 [ + + ]: 1107 : for (i = 0; i < num_tests; i++)
1777 : : {
1778 : : _stringlist *rl,
1779 : : *el,
1780 : : *tl;
5995 bruce@momjian.us 1781 : 849 : bool differ = false;
1782 : :
380 dgustafsson@postgres 1783 : 849 : INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]);
1784 : :
1785 : : /*
1786 : : * Advance over all three lists simultaneously.
1787 : : *
1788 : : * Compare resultfiles[j] with expectfiles[j] always. Tags are
1789 : : * optional but if there are tags, the tag list has the same
1790 : : * length as the other two lists.
1791 : : */
6151 magnus@hagander.net 1792 : 849 : for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
5995 bruce@momjian.us 1793 [ + + ]: 1826 : rl != NULL; /* rl and el have the same length */
2177 tgl@sss.pgh.pa.us 1794 : 977 : rl = rl->next, el = el->next,
2177 tgl@sss.pgh.pa.us 1795 :UBC 0 : tl = tl ? tl->next : NULL)
1796 : : {
1797 : : bool newdiff;
1798 : :
1189 tgl@sss.pgh.pa.us 1799 [ + + ]:CBC 977 : if (postfunc)
1800 : 192 : (*postfunc) (rl->str);
6151 magnus@hagander.net 1801 : 977 : newdiff = results_differ(tests[i], rl->str, el->str);
5995 bruce@momjian.us 1802 [ - + - - ]: 977 : if (newdiff && tl)
1803 : : {
380 dgustafsson@postgres 1804 :UBC 0 : diag("tag: %s", tl->str);
1805 : : }
6151 magnus@hagander.net 1806 [ + + ]:CBC 977 : differ |= newdiff;
1807 : : }
1808 : :
416 dgustafsson@postgres 1809 [ - + ]: 849 : if (statuses[i] != 0)
1810 : : {
380 dgustafsson@postgres 1811 :UBC 0 : test_status_failed(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1));
416 1812 : 0 : log_child_failure(statuses[i]);
1813 : : }
1814 : : else
1815 : : {
416 dgustafsson@postgres 1816 [ - + ]:CBC 849 : if (differ)
1817 : : {
380 dgustafsson@postgres 1818 :UBC 0 : test_status_failed(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1));
1819 : : }
1820 : : else
1821 : : {
380 dgustafsson@postgres 1822 :CBC 849 : test_status_ok(tests[i], INSTR_TIME_GET_MILLISEC(stoptimes[i]), (num_tests > 1));
1823 : : }
1824 : : }
1825 : : }
1826 : :
2381 tgl@sss.pgh.pa.us 1827 [ + + ]: 1107 : for (i = 0; i < num_tests; i++)
1828 : : {
1829 : 849 : pg_free(tests[i]);
1830 : 849 : tests[i] = NULL;
1831 : 849 : free_stringlist(&resultfiles[i]);
1832 : 849 : free_stringlist(&expectfiles[i]);
1833 : 849 : free_stringlist(&tags[i]);
1834 : : }
1835 : : }
1836 : :
6479 1837 : 5 : fclose(scf);
1838 : 5 : }
1839 : :
1840 : : /*
1841 : : * Run a single test
1842 : : */
1843 : : static void
1189 1844 : 303 : run_single_test(const char *test, test_start_function startfunc,
1845 : : postprocess_result_function postfunc)
1846 : : {
1847 : : PID_TYPE pid;
1848 : : instr_time starttime;
1849 : : instr_time stoptime;
1850 : : int exit_status;
6151 magnus@hagander.net 1851 : 303 : _stringlist *resultfiles = NULL;
1852 : 303 : _stringlist *expectfiles = NULL;
1853 : 303 : _stringlist *tags = NULL;
1854 : : _stringlist *rl,
1855 : : *el,
1856 : : *tl;
1857 : 303 : bool differ = false;
1858 : :
1189 tgl@sss.pgh.pa.us 1859 : 303 : pid = (startfunc) (test, &resultfiles, &expectfiles, &tags);
1890 1860 : 303 : INSTR_TIME_SET_CURRENT(starttime);
1861 : 303 : wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);
1862 : :
1863 : : /*
1864 : : * Advance over all three lists simultaneously.
1865 : : *
1866 : : * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
1867 : : * but if there are tags, the tag list has the same length as the other
1868 : : * two lists.
1869 : : */
6151 magnus@hagander.net 1870 : 303 : for (rl = resultfiles, el = expectfiles, tl = tags;
5995 bruce@momjian.us 1871 [ + + ]: 608 : rl != NULL; /* rl and el have the same length */
2177 tgl@sss.pgh.pa.us 1872 : 305 : rl = rl->next, el = el->next,
2177 tgl@sss.pgh.pa.us 1873 :UBC 0 : tl = tl ? tl->next : NULL)
1874 : : {
1875 : : bool newdiff;
1876 : :
1189 tgl@sss.pgh.pa.us 1877 [ + + ]:CBC 305 : if (postfunc)
1878 : 3 : (*postfunc) (rl->str);
6151 magnus@hagander.net 1879 : 305 : newdiff = results_differ(test, rl->str, el->str);
5995 bruce@momjian.us 1880 [ - + - - ]: 305 : if (newdiff && tl)
1881 : : {
380 dgustafsson@postgres 1882 :UBC 0 : diag("tag: %s", tl->str);
1883 : : }
6151 magnus@hagander.net 1884 [ + + ]:CBC 305 : differ |= newdiff;
1885 : : }
1886 : :
380 dgustafsson@postgres 1887 : 303 : INSTR_TIME_SUBTRACT(stoptime, starttime);
1888 : :
416 1889 [ - + ]: 303 : if (exit_status != 0)
1890 : : {
334 dgustafsson@postgres 1891 :UBC 0 : test_status_failed(test, INSTR_TIME_GET_MILLISEC(stoptime), false);
416 1892 : 0 : log_child_failure(exit_status);
1893 : : }
1894 : : else
1895 : : {
416 dgustafsson@postgres 1896 [ - + ]:CBC 303 : if (differ)
1897 : : {
334 dgustafsson@postgres 1898 :UBC 0 : test_status_failed(test, INSTR_TIME_GET_MILLISEC(stoptime), false);
1899 : : }
1900 : : else
1901 : : {
380 dgustafsson@postgres 1902 :CBC 303 : test_status_ok(test, INSTR_TIME_GET_MILLISEC(stoptime), false);
1903 : : }
1904 : : }
6479 tgl@sss.pgh.pa.us 1905 : 303 : }
1906 : :
1907 : : /*
1908 : : * Create the summary-output files (making them empty if already existing)
1909 : : */
1910 : : static void
1911 : 87 : open_result_files(void)
1912 : : {
1913 : : char file[MAXPGPATH];
1914 : : FILE *difffile;
1915 : :
1916 : : /* create outputdir directory if not present */
2588 andres@anarazel.de 1917 [ - + ]: 87 : if (!directory_exists(outputdir))
2588 andres@anarazel.de 1918 :UBC 0 : make_directory(outputdir);
1919 : :
1920 : : /* create the log file (copy of running status output) */
6479 tgl@sss.pgh.pa.us 1921 :CBC 87 : snprintf(file, sizeof(file), "%s/regression.out", outputdir);
2784 1922 : 87 : logfilename = pg_strdup(file);
6479 1923 : 87 : logfile = fopen(logfilename, "w");
1924 [ - + ]: 87 : if (!logfile)
10 michael@paquier.xyz 1925 :UNC 0 : bail("could not open file \"%s\" for writing: %m", logfilename);
1926 : :
1927 : : /* create the diffs file as empty */
6479 tgl@sss.pgh.pa.us 1928 :CBC 87 : snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
2784 1929 : 87 : difffilename = pg_strdup(file);
6479 1930 : 87 : difffile = fopen(difffilename, "w");
1931 [ - + ]: 87 : if (!difffile)
10 michael@paquier.xyz 1932 :UNC 0 : bail("could not open file \"%s\" for writing: %m", difffilename);
1933 : :
1934 : : /* we don't keep the diffs file open continuously */
6479 tgl@sss.pgh.pa.us 1935 :CBC 87 : fclose(difffile);
1936 : :
1937 : : /* also create the results directory if not present */
1938 : 87 : snprintf(file, sizeof(file), "%s/results", outputdir);
1939 [ + - ]: 87 : if (!directory_exists(file))
1940 : 87 : make_directory(file);
1941 : 87 : }
1942 : :
1943 : : static void
6151 magnus@hagander.net 1944 : 2 : drop_database_if_exists(const char *dbname)
1945 : : {
907 tgl@sss.pgh.pa.us 1946 : 2 : StringInfo buf = psql_start_command();
1947 : :
1948 : : /* Set warning level so we don't see chatter about nonexistent DB */
1949 : 2 : psql_add_command(buf, "SET client_min_messages = warning");
1950 : 2 : psql_add_command(buf, "DROP DATABASE IF EXISTS \"%s\"", dbname);
1951 : 2 : psql_end_command(buf, "postgres");
6151 magnus@hagander.net 1952 : 2 : }
1953 : :
1954 : : static void
1955 : 88 : create_database(const char *dbname)
1956 : : {
907 tgl@sss.pgh.pa.us 1957 : 88 : StringInfo buf = psql_start_command();
1958 : : _stringlist *sl;
1959 : :
1960 : : /*
1961 : : * We use template0 so that any installation-local cruft in template1 will
1962 : : * not mess up the tests.
1963 : : */
6151 magnus@hagander.net 1964 [ + + ]: 88 : if (encoding)
907 tgl@sss.pgh.pa.us 1965 :UBC 0 : psql_add_command(buf, "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
300 jdavis@postgresql.or 1966 [ + - ]:CBC 1 : (nolocale) ? " LOCALE='C'" : "");
1967 : : else
907 tgl@sss.pgh.pa.us 1968 :GBC 79 : psql_add_command(buf, "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
300 jdavis@postgresql.or 1969 [ + + ]:CBC 87 : (nolocale) ? " LOCALE='C'" : "");
907 tgl@sss.pgh.pa.us 1970 : 88 : psql_add_command(buf,
1971 : : "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
1972 : : "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
1973 : : "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
1974 : : "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
1975 : : "ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
1976 : : "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
1977 : : dbname, dbname, dbname, dbname, dbname, dbname);
1978 : 88 : psql_end_command(buf, "postgres");
1979 : :
1980 : : /*
1981 : : * Install any requested extensions. We use CREATE IF NOT EXISTS so that
1982 : : * this will work whether or not the extension is preinstalled.
1983 : : */
4790 1984 [ + + ]: 93 : for (sl = loadextension; sl != NULL; sl = sl->next)
1985 : 5 : psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
6151 magnus@hagander.net 1986 : 88 : }
1987 : :
1988 : : static void
6151 magnus@hagander.net 1989 :UBC 0 : drop_role_if_exists(const char *rolename)
1990 : : {
907 tgl@sss.pgh.pa.us 1991 : 0 : StringInfo buf = psql_start_command();
1992 : :
1993 : : /* Set warning level so we don't see chatter about nonexistent role */
1994 : 0 : psql_add_command(buf, "SET client_min_messages = warning");
1995 : 0 : psql_add_command(buf, "DROP ROLE IF EXISTS \"%s\"", rolename);
1996 : 0 : psql_end_command(buf, "postgres");
6151 magnus@hagander.net 1997 : 0 : }
1998 : :
1999 : : static void
3631 bruce@momjian.us 2000 :CBC 2 : create_role(const char *rolename, const _stringlist *granted_dbs)
2001 : : {
907 tgl@sss.pgh.pa.us 2002 : 2 : StringInfo buf = psql_start_command();
2003 : :
2004 : 2 : psql_add_command(buf, "CREATE ROLE \"%s\" WITH LOGIN", rolename);
6151 magnus@hagander.net 2005 [ + + ]: 6 : for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
2006 : : {
907 tgl@sss.pgh.pa.us 2007 : 4 : psql_add_command(buf, "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
2008 : 4 : granted_dbs->str, rolename);
2009 : : }
2010 : 2 : psql_end_command(buf, "postgres");
6151 magnus@hagander.net 2011 : 2 : }
2012 : :
2013 : : static void
6479 tgl@sss.pgh.pa.us 2014 :UBC 0 : help(void)
2015 : : {
2016 : 0 : printf(_("PostgreSQL regression test driver\n"));
2017 : 0 : printf(_("\n"));
4611 peter_e@gmx.net 2018 : 0 : printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
6479 tgl@sss.pgh.pa.us 2019 : 0 : printf(_("\n"));
2020 : 0 : printf(_("Options:\n"));
2375 mail@joeconway.com 2021 : 0 : printf(_(" --bindir=BINPATH use BINPATH for programs that are run;\n"));
2022 : 0 : printf(_(" if empty, use PATH from the environment\n"));
2023 : 0 : printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
2024 : 0 : printf(_(" --create-role=ROLE create the specified role before testing\n"));
2025 : 0 : printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
2026 : 0 : printf(_(" --debug turn on debug mode in programs that are run\n"));
2027 : 0 : printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
2028 : 0 : printf(_(" --encoding=ENCODING use ENCODING as the encoding\n"));
603 andres@anarazel.de 2029 : 0 : printf(_(" --expecteddir=DIR take expected files from DIR (default \".\")\n"));
2375 mail@joeconway.com 2030 : 0 : printf(_(" -h, --help show this help, then exit\n"));
2031 : 0 : printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n"));
2032 : 0 : printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
2033 : 0 : printf(_(" --load-extension=EXT load the named extension before running the\n"));
2034 : 0 : printf(_(" tests; can appear multiple times\n"));
2035 : 0 : printf(_(" --max-connections=N maximum number of concurrent connections\n"));
2036 : 0 : printf(_(" (default is 0, meaning unlimited)\n"));
2037 : 0 : printf(_(" --max-concurrent-tests=N maximum number of concurrent tests in schedule\n"));
2038 : 0 : printf(_(" (default is 0, meaning unlimited)\n"));
2039 : 0 : printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n"));
2040 : 0 : printf(_(" --schedule=FILE use test ordering schedule from FILE\n"));
2041 : 0 : printf(_(" (can be used multiple times to concatenate)\n"));
2042 : 0 : printf(_(" --temp-instance=DIR create a temporary instance in DIR\n"));
2043 : 0 : printf(_(" --use-existing use an existing installation\n"));
2044 : 0 : printf(_(" -V, --version output version information, then exit\n"));
3236 tgl@sss.pgh.pa.us 2045 : 0 : printf(_("\n"));
3279 peter_e@gmx.net 2046 : 0 : printf(_("Options for \"temp-instance\" mode:\n"));
2375 mail@joeconway.com 2047 : 0 : printf(_(" --no-locale use C locale\n"));
2048 : 0 : printf(_(" --port=PORT start postmaster on PORT\n"));
2049 : 0 : printf(_(" --temp-config=FILE append contents of FILE to temporary config\n"));
6479 tgl@sss.pgh.pa.us 2050 : 0 : printf(_("\n"));
2051 : 0 : printf(_("Options for using an existing installation:\n"));
2375 mail@joeconway.com 2052 : 0 : printf(_(" --host=HOST use postmaster running on HOST\n"));
2053 : 0 : printf(_(" --port=PORT use postmaster running at PORT\n"));
2054 : 0 : printf(_(" --user=USER connect as USER\n"));
6479 tgl@sss.pgh.pa.us 2055 : 0 : printf(_("\n"));
2056 : 0 : printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
2057 : 0 : printf(_("if the tests could not be run for some reason.\n"));
2058 : 0 : printf(_("\n"));
1507 peter@eisentraut.org 2059 : 0 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
2060 : 0 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
6479 tgl@sss.pgh.pa.us 2061 : 0 : }
2062 : :
2063 : : int
1189 tgl@sss.pgh.pa.us 2064 :CBC 352 : regression_main(int argc, char *argv[],
2065 : : init_function ifunc,
2066 : : test_start_function startfunc,
2067 : : postprocess_result_function postfunc)
2068 : : {
2069 : : static struct option long_options[] = {
2070 : : {"help", no_argument, NULL, 'h'},
2071 : : {"version", no_argument, NULL, 'V'},
2072 : : {"dbname", required_argument, NULL, 1},
2073 : : {"debug", no_argument, NULL, 2},
2074 : : {"inputdir", required_argument, NULL, 3},
2075 : : {"max-connections", required_argument, NULL, 5},
2076 : : {"encoding", required_argument, NULL, 6},
2077 : : {"outputdir", required_argument, NULL, 7},
2078 : : {"schedule", required_argument, NULL, 8},
2079 : : {"temp-instance", required_argument, NULL, 9},
2080 : : {"no-locale", no_argument, NULL, 10},
2081 : : {"host", required_argument, NULL, 13},
2082 : : {"port", required_argument, NULL, 14},
2083 : : {"user", required_argument, NULL, 15},
2084 : : {"bindir", required_argument, NULL, 16},
2085 : : {"dlpath", required_argument, NULL, 17},
2086 : : {"create-role", required_argument, NULL, 18},
2087 : : {"temp-config", required_argument, NULL, 19},
2088 : : {"use-existing", no_argument, NULL, 20},
2089 : : {"launcher", required_argument, NULL, 21},
2090 : : {"load-extension", required_argument, NULL, 22},
2091 : : {"config-auth", required_argument, NULL, 24},
2092 : : {"max-concurrent-tests", required_argument, NULL, 25},
2093 : : {"expecteddir", required_argument, NULL, 26},
2094 : : {NULL, 0, NULL, 0}
2095 : : };
2096 : :
2097 : : bool use_unix_sockets;
2098 : : _stringlist *sl;
2099 : : int c;
2100 : : int i;
2101 : : int option_index;
2102 : : char buf[MAXPGPATH * 4];
2103 : :
1840 peter@eisentraut.org 2104 : 352 : pg_logging_init(argv[0]);
6479 tgl@sss.pgh.pa.us 2105 : 352 : progname = get_progname(argv[0]);
5603 peter_e@gmx.net 2106 : 352 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
2107 : :
1840 peter@eisentraut.org 2108 : 352 : get_restricted_token();
2109 : :
4486 peter_e@gmx.net 2110 : 352 : atexit(stop_postmaster);
2111 : :
2112 : : #if defined(WIN32)
2113 : :
2114 : : /*
2115 : : * We don't use Unix-domain sockets on Windows by default (see comment at
2116 : : * remove_temp() for a reason). Override at your own risk.
2117 : : */
2118 : : use_unix_sockets = getenv("PG_TEST_USE_UNIX_SOCKETS") ? true : false;
2119 : : #else
1476 peter@eisentraut.org 2120 : 352 : use_unix_sockets = true;
2121 : : #endif
2122 : :
2123 [ - + ]: 352 : if (!use_unix_sockets)
1476 peter@eisentraut.org 2124 :UBC 0 : hostname = "localhost";
2125 : :
2126 : : /*
2127 : : * We call the initialization function here because that way we can set
2128 : : * default parameters and let them be overwritten by the commandline.
2129 : : */
3810 rhaas@postgresql.org 2130 :CBC 352 : ifunc(argc, argv);
2131 : :
4093 peter_e@gmx.net 2132 [ + - ]: 352 : if (getenv("PG_REGRESS_DIFF_OPTS"))
2133 : 352 : pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
2134 : :
6479 tgl@sss.pgh.pa.us 2135 [ + + ]: 1445 : while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
2136 : : {
2137 [ - - + - : 1093 : switch (c)
+ - + + +
+ + + + +
+ + + + -
- + + + +
- ]
2138 : : {
6479 tgl@sss.pgh.pa.us 2139 :UBC 0 : case 'h':
2140 : 0 : help();
4486 peter_e@gmx.net 2141 : 0 : exit(0);
6479 tgl@sss.pgh.pa.us 2142 : 0 : case 'V':
5898 2143 : 0 : puts("pg_regress (PostgreSQL) " PG_VERSION);
4486 peter_e@gmx.net 2144 : 0 : exit(0);
6479 tgl@sss.pgh.pa.us 2145 :CBC 86 : case 1:
2146 : :
2147 : : /*
2148 : : * If a default database was specified, we need to remove it
2149 : : * before we add the specified one.
2150 : : */
6151 magnus@hagander.net 2151 : 86 : free_stringlist(&dblist);
2810 peter_e@gmx.net 2152 : 86 : split_to_stringlist(optarg, ",", &dblist);
6479 tgl@sss.pgh.pa.us 2153 : 86 : break;
6479 tgl@sss.pgh.pa.us 2154 :UBC 0 : case 2:
2155 : 0 : debug = true;
2156 : 0 : break;
6479 tgl@sss.pgh.pa.us 2157 :CBC 87 : case 3:
2784 2158 : 87 : inputdir = pg_strdup(optarg);
6479 2159 : 87 : break;
6479 tgl@sss.pgh.pa.us 2160 :UBC 0 : case 5:
2161 : 0 : max_connections = atoi(optarg);
2162 : 0 : break;
6479 tgl@sss.pgh.pa.us 2163 :CBC 1 : case 6:
2784 2164 : 1 : encoding = pg_strdup(optarg);
6479 2165 : 1 : break;
2166 : 87 : case 7:
2784 2167 : 87 : outputdir = pg_strdup(optarg);
6479 2168 : 87 : break;
2169 : 5 : case 8:
2170 : 5 : add_stringlist_item(&schedulelist, optarg);
2171 : 5 : break;
2172 : 85 : case 9:
3279 peter_e@gmx.net 2173 : 85 : temp_instance = make_absolute_path(optarg);
6479 tgl@sss.pgh.pa.us 2174 : 85 : break;
2175 : 2 : case 10:
2176 : 2 : nolocale = true;
2177 : 2 : break;
2178 : 2 : case 13:
2784 2179 : 2 : hostname = pg_strdup(optarg);
6479 2180 : 2 : break;
2181 : 87 : case 14:
2182 : 87 : port = atoi(optarg);
5616 peter_e@gmx.net 2183 : 87 : port_specified_by_user = true;
6479 tgl@sss.pgh.pa.us 2184 : 87 : break;
2185 : 3 : case 15:
2784 2186 : 3 : user = pg_strdup(optarg);
6479 2187 : 3 : break;
6477 2188 : 87 : case 16:
2189 : : /* "--bindir=" means to use PATH */
2190 [ - + ]: 87 : if (strlen(optarg))
2784 tgl@sss.pgh.pa.us 2191 :UBC 0 : bindir = pg_strdup(optarg);
2192 : : else
3279 peter_e@gmx.net 2193 :CBC 87 : bindir = NULL;
6477 tgl@sss.pgh.pa.us 2194 : 87 : break;
6295 alvherre@alvh.no-ip. 2195 : 90 : case 17:
2784 tgl@sss.pgh.pa.us 2196 : 90 : dlpath = pg_strdup(optarg);
6295 alvherre@alvh.no-ip. 2197 : 90 : break;
6151 magnus@hagander.net 2198 : 22 : case 18:
2810 peter_e@gmx.net 2199 : 22 : split_to_stringlist(optarg, ",", &extraroles);
6151 magnus@hagander.net 2200 : 22 : break;
6062 andrew@dunslane.net 2201 : 7 : case 19:
2968 2202 : 7 : add_stringlist_item(&temp_configs, optarg);
6062 2203 : 7 : break;
5230 simon@2ndQuadrant.co 2204 :UBC 0 : case 20:
2205 : 0 : use_existing = true;
2206 : 0 : break;
4830 rhaas@postgresql.org 2207 : 0 : case 21:
2784 tgl@sss.pgh.pa.us 2208 : 0 : launcher = pg_strdup(optarg);
4830 rhaas@postgresql.org 2209 : 0 : break;
4790 tgl@sss.pgh.pa.us 2210 :CBC 5 : case 22:
2211 : 5 : add_stringlist_item(&loadextension, optarg);
2212 : 5 : break;
3406 noah@leadboat.com 2213 : 265 : case 24:
2784 tgl@sss.pgh.pa.us 2214 : 265 : config_auth_datadir = pg_strdup(optarg);
3406 noah@leadboat.com 2215 : 265 : break;
2381 tgl@sss.pgh.pa.us 2216 : 87 : case 25:
2217 : 87 : max_concurrent_tests = atoi(optarg);
2218 : 87 : break;
603 andres@anarazel.de 2219 : 85 : case 26:
2220 : 85 : expecteddir = pg_strdup(optarg);
2221 : 85 : break;
6479 tgl@sss.pgh.pa.us 2222 :UBC 0 : default:
2223 : : /* getopt_long already emitted a complaint */
380 dgustafsson@postgres 2224 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.",
2225 : : progname);
4486 peter_e@gmx.net 2226 : 0 : exit(2);
2227 : : }
2228 : : }
2229 : :
2230 : : /*
2231 : : * if we still have arguments, they are extra tests to run
2232 : : */
6479 tgl@sss.pgh.pa.us 2233 [ + + ]:CBC 655 : while (argc - optind >= 1)
2234 : : {
2235 : 303 : add_stringlist_item(&extra_tests, argv[optind]);
2236 : 303 : optind++;
2237 : : }
2238 : :
2239 : : /*
2240 : : * We must have a database to run the tests in; either a default name, or
2241 : : * one supplied by the --dbname switch.
2242 : : */
501 2243 [ + - + - : 352 : if (!(dblist && dblist->str && dblist->str[0]))
- + ]
2244 : : {
380 dgustafsson@postgres 2245 :UBC 0 : bail("no database name was specified");
2246 : : }
2247 : :
3406 noah@leadboat.com 2248 [ + + ]:CBC 352 : if (config_auth_datadir)
2249 : : {
2250 : : #ifdef ENABLE_SSPI
2251 : : if (!use_unix_sockets)
2252 : : config_sspi_auth(config_auth_datadir, user);
2253 : : #endif
2254 : 265 : exit(0);
2255 : : }
2256 : :
3279 peter_e@gmx.net 2257 [ + + - + ]: 87 : if (temp_instance && !port_specified_by_user)
2258 : :
2259 : : /*
2260 : : * To reduce chances of interference with parallel installations, use
2261 : : * a port number starting in the private range (49152-65535)
2262 : : * calculated from the version number. This aids non-Unix socket mode
2263 : : * systems; elsewhere, the use of a private socket directory already
2264 : : * prevents interference.
2265 : : */
5616 peter_e@gmx.net 2266 :UBC 0 : port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
2267 : :
1877 peter@eisentraut.org 2268 :CBC 87 : inputdir = make_absolute_path(inputdir);
2269 : 87 : outputdir = make_absolute_path(outputdir);
603 andres@anarazel.de 2270 : 87 : expecteddir = make_absolute_path(expecteddir);
1877 peter@eisentraut.org 2271 : 87 : dlpath = make_absolute_path(dlpath);
2272 : :
2273 : : /*
2274 : : * Initialization
2275 : : */
6479 tgl@sss.pgh.pa.us 2276 : 87 : open_result_files();
2277 : :
2278 : 87 : initialize_environment();
2279 : :
2280 : : #if defined(HAVE_GETRLIMIT)
6309 andrew@dunslane.net 2281 : 87 : unlimit_core_size();
2282 : : #endif
2283 : :
3279 peter_e@gmx.net 2284 [ + + ]: 87 : if (temp_instance)
2285 : : {
2286 : : StringInfoData cmd;
2287 : : FILE *pg_conf;
2288 : : const char *env_wait;
2289 : : int wait_seconds;
2290 : : const char *initdb_template_dir;
2291 : : const char *keywords[4];
2292 : : const char *values[4];
2293 : : PGPing rv;
2294 : : const char *initdb_extra_opts_env;
2295 : :
2296 : : /*
2297 : : * Prepare the temp instance
2298 : : */
2299 : :
2300 [ - + ]: 85 : if (directory_exists(temp_instance))
2301 : : {
3279 peter_e@gmx.net 2302 [ # # ]:UBC 0 : if (!rmtree(temp_instance, true))
2303 : : {
380 dgustafsson@postgres 2304 : 0 : bail("could not remove temp instance \"%s\"", temp_instance);
2305 : : }
2306 : : }
2307 : :
2308 : : /* make the temp instance top directory */
3279 peter_e@gmx.net 2309 :CBC 85 : make_directory(temp_instance);
2310 : :
2311 : : /* and a directory for log files */
3190 andrew@dunslane.net 2312 : 85 : snprintf(buf, sizeof(buf), "%s/log", outputdir);
6479 tgl@sss.pgh.pa.us 2313 [ + - ]: 85 : if (!directory_exists(buf))
2314 : 85 : make_directory(buf);
2315 : :
59 peter@eisentraut.org 2316 :GNC 85 : initdb_extra_opts_env = getenv("PG_TEST_INITDB_EXTRA_OPTS");
2317 : :
284 2318 : 85 : initStringInfo(&cmd);
2319 : :
2320 : : /*
2321 : : * Create data directory.
2322 : : *
2323 : : * If available, use a previously initdb'd cluster as a template by
2324 : : * copying it. For a lot of tests, that's substantially cheaper.
2325 : : *
2326 : : * There's very similar code in Cluster.pm, but we can't easily de
2327 : : * duplicate it until we require perl at build time.
2328 : : */
234 andres@anarazel.de 2329 : 85 : initdb_template_dir = getenv("INITDB_TEMPLATE");
59 peter@eisentraut.org 2330 [ + - + + : 85 : if (initdb_template_dir == NULL || nolocale || debug || initdb_extra_opts_env)
+ - - + ]
2331 : : {
234 andres@anarazel.de 2332 : 2 : note("initializing database system by running initdb");
2333 : :
234 andres@anarazel.de 2334 :UNC 0 : appendStringInfo(&cmd,
2335 : : "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync",
234 andres@anarazel.de 2336 [ - + ]:GNC 2 : bindir ? bindir : "",
2337 [ - + ]: 2 : bindir ? "/" : "",
2338 : : temp_instance);
2339 [ - + ]: 2 : if (debug)
194 drowley@postgresql.o 2340 :UNC 0 : appendStringInfoString(&cmd, " --debug");
234 andres@anarazel.de 2341 [ + - ]:GNC 2 : if (nolocale)
194 drowley@postgresql.o 2342 : 2 : appendStringInfoString(&cmd, " --no-locale");
59 peter@eisentraut.org 2343 [ - + ]: 2 : if (initdb_extra_opts_env)
59 peter@eisentraut.org 2344 :UNC 0 : appendStringInfo(&cmd, " %s", initdb_extra_opts_env);
234 andres@anarazel.de 2345 :GNC 2 : appendStringInfo(&cmd, " > \"%s/log/initdb.log\" 2>&1", outputdir);
2346 : 2 : fflush(NULL);
2347 [ - + ]: 2 : if (system(cmd.data))
2348 : : {
234 andres@anarazel.de 2349 :UNC 0 : bail("initdb failed\n"
2350 : : "# Examine \"%s/log/initdb.log\" for the reason.\n"
2351 : : "# Command was: %s",
2352 : : outputdir, cmd.data);
2353 : : }
2354 : : }
2355 : : else
2356 : : {
2357 : : #ifndef WIN32
233 andres@anarazel.de 2358 :GNC 83 : const char *copycmd = "cp -RPp \"%s\" \"%s/data\"";
234 2359 : 83 : int expected_exitcode = 0;
2360 : : #else
2361 : : const char *copycmd = "robocopy /E /NJS /NJH /NFL /NDL /NP \"%s\" \"%s/data\"";
2362 : : int expected_exitcode = 1; /* 1 denotes files were copied */
2363 : : #endif
2364 : :
2365 : 83 : note("initializing database system by copying initdb template");
2366 : :
2367 : 83 : appendStringInfo(&cmd,
2368 : : copycmd,
2369 : : initdb_template_dir,
2370 : : temp_instance);
2371 : 83 : appendStringInfo(&cmd, " > \"%s/log/initdb.log\" 2>&1", outputdir);
2372 : 83 : fflush(NULL);
2373 [ - + ]: 83 : if (system(cmd.data) != expected_exitcode)
2374 : : {
234 andres@anarazel.de 2375 :UNC 0 : bail("copying of initdb template failed\n"
2376 : : "# Examine \"%s/log/initdb.log\" for the reason.\n"
2377 : : "# Command was: %s",
2378 : : outputdir, cmd.data);
2379 : : }
2380 : : }
2381 : :
284 peter@eisentraut.org 2382 :GNC 85 : pfree(cmd.data);
2383 : :
2384 : : /*
2385 : : * Adjust the default postgresql.conf for regression testing. The user
2386 : : * can specify a file to be appended; in any case we expand logging
2387 : : * and set max_prepared_transactions to enable testing of prepared
2388 : : * xacts. (Note: to reduce the probability of unexpected shmmax
2389 : : * failures, don't set max_prepared_transactions any higher than
2390 : : * actually needed by the prepared_xacts regression test.)
2391 : : */
3279 peter_e@gmx.net 2392 :CBC 85 : snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
5470 tgl@sss.pgh.pa.us 2393 : 85 : pg_conf = fopen(buf, "a");
2394 [ - + ]: 85 : if (pg_conf == NULL)
10 michael@paquier.xyz 2395 :UNC 0 : bail("could not open \"%s\" for adding extra config: %m", buf);
2396 : :
5470 tgl@sss.pgh.pa.us 2397 :CBC 85 : fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
3374 noah@leadboat.com 2398 : 85 : fputs("log_autovacuum_min_duration = 0\n", pg_conf);
2399 : 85 : fputs("log_checkpoints = on\n", pg_conf);
1491 peter@eisentraut.org 2400 : 85 : fputs("log_line_prefix = '%m %b[%p] %q%a '\n", pg_conf);
3374 noah@leadboat.com 2401 : 85 : fputs("log_lock_waits = on\n", pg_conf);
2402 : 85 : fputs("log_temp_files = 128kB\n", pg_conf);
5470 tgl@sss.pgh.pa.us 2403 : 85 : fputs("max_prepared_transactions = 2\n", pg_conf);
2404 : :
2968 andrew@dunslane.net 2405 [ + + ]: 92 : for (sl = temp_configs; sl != NULL; sl = sl->next)
2406 : : {
2407 : 7 : char *temp_config = sl->str;
2408 : : FILE *extra_conf;
2409 : : char line_buf[1024];
2410 : :
5995 bruce@momjian.us 2411 : 7 : extra_conf = fopen(temp_config, "r");
6062 andrew@dunslane.net 2412 [ - + ]: 7 : if (extra_conf == NULL)
2413 : : {
10 michael@paquier.xyz 2414 :UNC 0 : bail("could not open \"%s\" to read extra config: %m",
2415 : : temp_config);
2416 : : }
5995 bruce@momjian.us 2417 [ + + ]:CBC 25 : while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
6062 andrew@dunslane.net 2418 : 18 : fputs(line_buf, pg_conf);
2419 : 7 : fclose(extra_conf);
2420 : : }
2421 : :
5470 tgl@sss.pgh.pa.us 2422 : 85 : fclose(pg_conf);
2423 : :
2424 : : #ifdef ENABLE_SSPI
2425 : : if (!use_unix_sockets)
2426 : : {
2427 : : /*
2428 : : * Since we successfully used the same buffer for the much-longer
2429 : : * "initdb" command, this can't truncate.
2430 : : */
2431 : : snprintf(buf, sizeof(buf), "%s/data", temp_instance);
2432 : : config_sspi_auth(buf, NULL);
2433 : : }
2434 : : #endif
2435 : :
2436 : : /*
2437 : : * Prepare the connection params for checking the state of the server
2438 : : * before starting the tests.
2439 : : */
173 dgustafsson@postgres 2440 :GNC 85 : sprintf(portstr, "%d", port);
2441 : 85 : keywords[0] = "dbname";
2442 : 85 : values[0] = "postgres";
2443 : 85 : keywords[1] = "port";
2444 : 85 : values[1] = portstr;
2445 : 85 : keywords[2] = "host";
2446 [ - + ]: 85 : values[2] = hostname ? hostname : sockdir;
2447 : 85 : keywords[3] = NULL;
2448 : 85 : values[3] = NULL;
2449 : :
2450 : : /*
2451 : : * Check if there is a postmaster running already.
2452 : : */
5616 peter_e@gmx.net 2453 [ + - ]:CBC 85 : for (i = 0; i < 16; i++)
2454 : : {
173 dgustafsson@postgres 2455 :GNC 85 : rv = PQpingParams(keywords, values, 1);
2456 : :
2457 [ - + ]: 85 : if (rv == PQPING_OK)
2458 : : {
5616 peter_e@gmx.net 2459 [ # # # # ]:UBC 0 : if (port_specified_by_user || i == 15)
2460 : : {
380 dgustafsson@postgres 2461 : 0 : note("port %d apparently in use", port);
5616 peter_e@gmx.net 2462 [ # # ]: 0 : if (!port_specified_by_user)
380 dgustafsson@postgres 2463 : 0 : note("could not determine an available port");
2464 : 0 : bail("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.");
2465 : : }
2466 : :
2467 : 0 : note("port %d apparently in use, trying %d", port, port + 1);
5616 peter_e@gmx.net 2468 : 0 : port++;
173 dgustafsson@postgres 2469 :UNC 0 : sprintf(portstr, "%d", port);
2470 : 0 : setenv("PGPORT", portstr, 1);
2471 : : }
2472 : : else
5616 peter_e@gmx.net 2473 :CBC 85 : break;
2474 : : }
2475 : :
2476 : : /*
2477 : : * Start the temp postmaster
2478 : : */
6479 tgl@sss.pgh.pa.us 2479 :UBC 0 : snprintf(buf, sizeof(buf),
2480 : : "\"%s%spostgres\" -D \"%s/data\" -F%s "
2481 : : "-c \"listen_addresses=%s\" -k \"%s\" "
2482 : : "> \"%s/log/postmaster.log\" 2>&1",
3279 peter_e@gmx.net 2483 [ - + ]:CBC 85 : bindir ? bindir : "",
2484 [ - + ]: 85 : bindir ? "/" : "",
2485 [ - + ]: 85 : temp_instance, debug ? " -d 5" : "",
3592 noah@leadboat.com 2486 [ + - - + ]: 170 : hostname ? hostname : "", sockdir ? sockdir : "",
2487 : : outputdir);
6479 tgl@sss.pgh.pa.us 2488 : 85 : postmaster_pid = spawn_process(buf);
2489 [ - + ]: 85 : if (postmaster_pid == INVALID_PID)
10 michael@paquier.xyz 2490 :UNC 0 : bail("could not spawn postmaster: %m");
2491 : :
2492 : : /*
2493 : : * Wait till postmaster is able to accept connections; normally takes
2494 : : * only a fraction of a second or so, but Cygwin is reportedly *much*
2495 : : * slower, and test builds using Valgrind or similar tools might be
2496 : : * too. Hence, allow the default timeout of 60 seconds to be
2497 : : * overridden from the PGCTLTIMEOUT environment variable.
2498 : : */
2916 tgl@sss.pgh.pa.us 2499 :CBC 85 : env_wait = getenv("PGCTLTIMEOUT");
2500 [ - + ]: 85 : if (env_wait != NULL)
2501 : : {
2916 tgl@sss.pgh.pa.us 2502 :UBC 0 : wait_seconds = atoi(env_wait);
2503 [ # # ]: 0 : if (wait_seconds <= 0)
2504 : 0 : wait_seconds = 60;
2505 : : }
2506 : : else
2916 tgl@sss.pgh.pa.us 2507 :CBC 85 : wait_seconds = 60;
2508 : :
173 dgustafsson@postgres 2509 [ + - ]:GNC 194 : for (i = 0; i < wait_seconds * WAIT_TICKS_PER_SECOND; i++)
2510 : : {
2511 : : /*
2512 : : * It's fairly unlikely that the server is responding immediately
2513 : : * so we start with sleeping before checking instead of the other
2514 : : * way around.
2515 : : */
2516 : 194 : pg_usleep(1000000L / WAIT_TICKS_PER_SECOND);
2517 : :
2518 : 194 : rv = PQpingParams(keywords, values, 1);
2519 : :
2520 : : /* Done if the server is running and accepts connections */
2521 [ + + ]: 194 : if (rv == PQPING_OK)
6479 tgl@sss.pgh.pa.us 2522 :CBC 85 : break;
2523 : :
173 dgustafsson@postgres 2524 [ - + ]:GNC 109 : if (rv == PQPING_NO_ATTEMPT)
173 dgustafsson@postgres 2525 :UNC 0 : bail("attempting to connect to postmaster failed");
2526 : :
2527 : : /*
2528 : : * Fail immediately if postmaster has exited
2529 : : */
2530 : : #ifndef WIN32
1931 noah@leadboat.com 2531 [ - + ]:CBC 109 : if (waitpid(postmaster_pid, NULL, WNOHANG) == postmaster_pid)
2532 : : #else
2533 : : if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
2534 : : #endif
2535 : : {
380 dgustafsson@postgres 2536 :UBC 0 : bail("postmaster failed, examine \"%s/log/postmaster.log\" for the reason",
2537 : : outputdir);
2538 : : }
2539 : : }
173 dgustafsson@postgres 2540 [ - + ]:GNC 85 : if (i >= wait_seconds * WAIT_TICKS_PER_SECOND)
2541 : : {
380 dgustafsson@postgres 2542 :UBC 0 : diag("postmaster did not respond within %d seconds, examine \"%s/log/postmaster.log\" for the reason",
2543 : : wait_seconds, outputdir);
2544 : :
2545 : : /*
2546 : : * If we get here, the postmaster is probably wedged somewhere in
2547 : : * startup. Try to kill it ungracefully rather than leaving a
2548 : : * stuck postmaster that might interfere with subsequent test
2549 : : * attempts.
2550 : : */
2551 : : #ifndef WIN32
2552 [ # # # # ]: 0 : if (kill(postmaster_pid, SIGKILL) != 0 && errno != ESRCH)
10 michael@paquier.xyz 2553 :UNC 0 : bail("could not kill failed postmaster: %m");
2554 : : #else
2555 : : if (TerminateProcess(postmaster_pid, 255) == 0)
2556 : : bail("could not kill failed postmaster: error code %lu",
2557 : : GetLastError());
2558 : : #endif
380 dgustafsson@postgres 2559 :UBC 0 : bail("postmaster failed");
2560 : : }
2561 : :
6479 tgl@sss.pgh.pa.us 2562 :CBC 85 : postmaster_running = true;
2563 : :
2564 : : #ifdef _WIN64
2565 : : /* need a series of two casts to convert HANDLE without compiler warning */
2566 : : #define ULONGPID(x) (unsigned long) (unsigned long long) (x)
2567 : : #else
2568 : : #define ULONGPID(x) (unsigned long) (x)
2569 : : #endif
380 dgustafsson@postgres 2570 : 85 : note("using temp instance on port %d with PID %lu",
2571 : : port, ULONGPID(postmaster_pid));
2572 : : }
2573 : : else
2574 : : {
2575 : : /*
2576 : : * Using an existing installation, so may need to get rid of
2577 : : * pre-existing database(s) and role(s)
2578 : : */
5230 simon@2ndQuadrant.co 2579 [ + - ]: 2 : if (!use_existing)
2580 : : {
2581 [ + + ]: 4 : for (sl = dblist; sl; sl = sl->next)
2582 : 2 : drop_database_if_exists(sl->str);
2583 [ - + ]: 2 : for (sl = extraroles; sl; sl = sl->next)
5230 simon@2ndQuadrant.co 2584 :UBC 0 : drop_role_if_exists(sl->str);
2585 : : }
2586 : : }
2587 : :
2588 : : /*
2589 : : * Create the test database(s) and role(s)
2590 : : */
5230 simon@2ndQuadrant.co 2591 [ + - ]:CBC 87 : if (!use_existing)
2592 : : {
2593 [ + + ]: 175 : for (sl = dblist; sl; sl = sl->next)
2594 : 88 : create_database(sl->str);
2595 [ + + ]: 89 : for (sl = extraroles; sl; sl = sl->next)
2596 : 2 : create_role(sl->str, dblist);
2597 : : }
2598 : :
2599 : : /*
2600 : : * Ready to run the tests
2601 : : */
6479 tgl@sss.pgh.pa.us 2602 [ + + ]: 92 : for (sl = schedulelist; sl != NULL; sl = sl->next)
2603 : : {
1189 2604 : 5 : run_schedule(sl->str, startfunc, postfunc);
2605 : : }
2606 : :
6479 2607 [ + + ]: 390 : for (sl = extra_tests; sl != NULL; sl = sl->next)
2608 : : {
1189 2609 : 303 : run_single_test(sl->str, startfunc, postfunc);
2610 : : }
2611 : :
2612 : : /*
2613 : : * Shut down temp installation's postmaster
2614 : : */
3279 peter_e@gmx.net 2615 [ + + ]: 87 : if (temp_instance)
2616 : : {
6479 tgl@sss.pgh.pa.us 2617 : 85 : stop_postmaster();
2618 : : }
2619 : :
2620 : : /*
2621 : : * If there were no errors, remove the temp instance immediately to
2622 : : * conserve disk space. (If there were errors, we leave the instance in
2623 : : * place for possible manual investigation.)
2624 : : */
461 2625 [ + + + - ]: 87 : if (temp_instance && fail_count == 0)
2626 : : {
3279 peter_e@gmx.net 2627 [ - + ]: 85 : if (!rmtree(temp_instance, true))
380 dgustafsson@postgres 2628 :UBC 0 : diag("could not remove temp instance \"%s\"",
2629 : : temp_instance);
2630 : : }
2631 : :
2632 : : /*
2633 : : * Emit a TAP compliant Plan
2634 : : */
380 dgustafsson@postgres 2635 :CBC 87 : plan(fail_count + success_count);
2636 : :
2637 : : /*
2638 : : * Emit nice-looking summary message
2639 : : */
461 tgl@sss.pgh.pa.us 2640 [ + - ]: 87 : if (fail_count == 0)
380 dgustafsson@postgres 2641 : 87 : note("All %d tests passed.", success_count);
2642 : : else
380 dgustafsson@postgres 2643 :UBC 0 : diag("%d of %d tests failed.", fail_count, success_count + fail_count);
2644 : :
6479 tgl@sss.pgh.pa.us 2645 [ - + ]:CBC 87 : if (file_size(difffilename) > 0)
2646 : : {
380 dgustafsson@postgres 2647 :UBC 0 : diag("The differences that caused some tests to fail can be viewed in the file \"%s\".",
2648 : : difffilename);
2649 : 0 : diag("A copy of the test summary that you see above is saved in the file \"%s\".",
2650 : : logfilename);
2651 : : }
2652 : : else
2653 : : {
6479 tgl@sss.pgh.pa.us 2654 :CBC 87 : unlink(difffilename);
2655 : 87 : unlink(logfilename);
2656 : : }
2657 : :
380 dgustafsson@postgres 2658 : 87 : fclose(logfile);
2659 : 87 : logfile = NULL;
2660 : :
6479 tgl@sss.pgh.pa.us 2661 [ - + ]: 87 : if (fail_count != 0)
4486 peter_e@gmx.net 2662 :UBC 0 : exit(1);
2663 : :
6479 tgl@sss.pgh.pa.us 2664 :CBC 87 : return 0;
2665 : : }
|