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