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

Generated by: LCOV version 2.0-1