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