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