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

Generated by: LCOV version 1.13