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

Generated by: LCOV version 1.14