LCOV - code coverage report
Current view: top level - src/test/regress - pg_regress.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta1 Lines: 635 919 69.1 %
Date: 2019-06-15 23:07:03 Functions: 36 40 90.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13