LCOV - code coverage report
Current view: top level - src/test/regress - pg_regress.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 511 899 56.8 %
Date: 2017-09-29 15:12:54 Functions: 32 41 78.0 %
Legend: Lines: hit not hit

          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         363 : add_stringlist_item(_stringlist **listhead, const char *str)
     155             : {
     156         363 :     _stringlist *newentry = pg_malloc(sizeof(_stringlist));
     157             :     _stringlist *oldentry;
     158             : 
     159         363 :     newentry->str = pg_strdup(str);
     160         363 :     newentry->next = NULL;
     161         363 :     if (*listhead == NULL)
     162         363 :         *listhead = newentry;
     163             :     else
     164             :     {
     165           0 :         for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
     166             :              /* skip */ ;
     167           0 :         oldentry->next = newentry;
     168             :     }
     169         363 : }
     170             : 
     171             : /*
     172             :  * Free a stringlist.
     173             :  */
     174             : static void
     175         538 : free_stringlist(_stringlist **listhead)
     176             : {
     177         538 :     if (listhead == NULL || *listhead == NULL)
     178         717 :         return;
     179         359 :     if ((*listhead)->next != NULL)
     180           0 :         free_stringlist(&((*listhead)->next));
     181         359 :     free((*listhead)->str);
     182         359 :     free(*listhead);
     183         359 :     *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         538 : status(const char *fmt,...)
     225             : {
     226             :     va_list     ap;
     227             : 
     228         538 :     va_start(ap, fmt);
     229         538 :     vfprintf(stdout, fmt, ap);
     230         538 :     fflush(stdout);
     231         538 :     va_end(ap);
     232             : 
     233         538 :     if (logfile)
     234             :     {
     235         538 :         va_start(ap, fmt);
     236         538 :         vfprintf(logfile, fmt, ap);
     237         538 :         va_end(ap);
     238             :     }
     239         538 : }
     240             : 
     241             : /*
     242             :  * Done "doing something ..."
     243             :  */
     244             : static void
     245         194 : status_end(void)
     246             : {
     247         194 :     fprintf(stdout, "\n");
     248         194 :     fflush(stdout);
     249         194 :     if (logfile)
     250         194 :         fprintf(logfile, "\n");
     251         194 : }
     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         180 : 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         180 :     if (!file || !(file_type = strrchr(file, '.')))
     693           0 :         return NULL;
     694             : 
     695         180 :     file_type++;
     696             : 
     697         180 :     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         180 :     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         181 : 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         181 :     fflush(stdout);
    1136         181 :     fflush(stderr);
    1137         181 :     if (logfile)
    1138         181 :         fflush(logfile);
    1139             : 
    1140         181 :     pid = fork();
    1141         362 :     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         362 :     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         181 :         cmdline2 = psprintf("exec %s", cmdline);
    1159         181 :         execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
    1160           0 :         fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
    1161         181 :                 progname, shellprog, strerror(errno));
    1162           0 :         _exit(1);               /* not exit() here... */
    1163             :     }
    1164             :     /* in parent */
    1165         181 :     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         374 : file_exists(const char *file)
    1231             : {
    1232         374 :     FILE       *f = fopen(file, "r");
    1233             : 
    1234         374 :     if (!f)
    1235           6 :         return false;
    1236         368 :     fclose(f);
    1237         368 :     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         188 : run_diff(const char *cmd, const char *filename)
    1303             : {
    1304             :     int         r;
    1305             : 
    1306         188 :     r = system(cmd);
    1307         188 :     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         188 :     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         180 : 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         180 :     platform_expectfile = get_expectfile(testname, resultsfile);
    1352             : 
    1353         180 :     strlcpy(expectfile, default_expectfile, sizeof(expectfile));
    1354         180 :     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         180 :     snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
    1368             : 
    1369             :     /* OK, run the diff */
    1370         180 :     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         180 :     if (run_diff(cmd, diff) == 0)
    1376             :     {
    1377         174 :         unlink(diff);
    1378         174 :         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         240 :     while (tests_left > 0)
    1495             :     {
    1496             :         PID_TYPE    p;
    1497             : 
    1498             : #ifndef WIN32
    1499             :         int         exit_status;
    1500             : 
    1501         180 :         p = wait(&exit_status);
    1502             : 
    1503         180 :         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        1471 :         for (i = 0; i < num_tests; i++)
    1526             :         {
    1527        1471 :             if (p == pids[i])
    1528             :             {
    1529             : #ifdef WIN32
    1530             :                 GetExitCodeProcess(pids[i], &exit_status);
    1531             :                 CloseHandle(pids[i]);
    1532             : #endif
    1533         180 :                 pids[i] = INVALID_PID;
    1534         180 :                 statuses[i] = (int) exit_status;
    1535         180 :                 if (names)
    1536         164 :                     status(" %s", names[i]);
    1537         180 :                 tests_left--;
    1538         180 :                 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         302 :         for (i = 0; i < MAX_PARALLEL_TESTS; i++)
    1618             :         {
    1619         302 :             if (resultfiles[i] == NULL)
    1620         123 :                 break;
    1621         179 :             free_stringlist(&resultfiles[i]);
    1622         179 :             free_stringlist(&expectfiles[i]);
    1623         179 :             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        1722 :         for (c = test; *c; c++)
    1659             :         {
    1660        1692 :             if (isspace((unsigned char) *c))
    1661             :             {
    1662         150 :                 *c = '\0';
    1663         150 :                 inword = false;
    1664             :             }
    1665        1542 :             else if (!inword)
    1666             :             {
    1667         180 :                 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         180 :                 tests[num_tests] = c;
    1675         180 :                 num_tests++;
    1676         180 :                 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         178 :             for (i = 0; i < num_tests; i++)
    1718             :             {
    1719         164 :                 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         210 :         for (i = 0; i < num_tests; i++)
    1727             :         {
    1728             :             _stringlist *rl,
    1729             :                        *el,
    1730             :                        *tl;
    1731         180 :             bool        differ = false;
    1732             : 
    1733         180 :             if (num_tests > 1)
    1734         164 :                 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         540 :             for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
    1744             :                  rl != NULL;    /* rl and el have the same length */
    1745         180 :                  rl = rl->next, el = el->next)
    1746             :             {
    1747             :                 bool        newdiff;
    1748             : 
    1749         180 :                 if (tl)
    1750           0 :                     tl = tl->next;   /* tl has the same length as rl and el if
    1751             :                                      * it exists */
    1752             : 
    1753         180 :                 newdiff = results_differ(tests[i], rl->str, el->str);
    1754         180 :                 if (newdiff && tl)
    1755             :                 {
    1756           0 :                     printf("%s ", tl->str);
    1757             :                 }
    1758         180 :                 differ |= newdiff;
    1759             :             }
    1760             : 
    1761         180 :             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         180 :                 status(_("ok"));
    1788         180 :                 success_count++;
    1789             :             }
    1790             : 
    1791         180 :             if (statuses[i] != 0)
    1792           0 :                 log_child_failure(statuses[i]);
    1793             : 
    1794         180 :             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             : }

Generated by: LCOV version 1.11