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

Generated by: LCOV version 1.13