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