LCOV - code coverage report
Current view: top level - src/test/regress - pg_regress.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12devel Lines: 631 928 68.0 %
Date: 2019-02-21 15:20:26 Functions: 36 41 87.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13