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