LCOV - code coverage report
Current view: top level - src/test/regress - pg_regress.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 663 948 69.9 %
Date: 2020-11-27 12:05:55 Functions: 36 40 90.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13