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

Generated by: LCOV version 1.13