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