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