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