Header And Logo

PostgreSQL
| The world's most advanced open source database.

pg_regress.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pg_regress --- regression test driver
00004  *
00005  * This is a C implementation of the previous shell script for running
00006  * the regression tests, and should be mostly compatible with it.
00007  * Initial author of C translation: Magnus Hagander
00008  *
00009  * This code is released under the terms of the PostgreSQL License.
00010  *
00011  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00012  * Portions Copyright (c) 1994, Regents of the University of California
00013  *
00014  * src/test/regress/pg_regress.c
00015  *
00016  *-------------------------------------------------------------------------
00017  */
00018 
00019 #include "pg_regress.h"
00020 
00021 #include <ctype.h>
00022 #include <sys/stat.h>
00023 #include <sys/wait.h>
00024 #include <signal.h>
00025 #include <unistd.h>
00026 
00027 #ifdef HAVE_SYS_RESOURCE_H
00028 #include <sys/time.h>
00029 #include <sys/resource.h>
00030 #endif
00031 
00032 #include "getopt_long.h"
00033 #include "pg_config_paths.h"
00034 
00035 /* for resultmap we need a list of pairs of strings */
00036 typedef struct _resultmap
00037 {
00038     char       *test;
00039     char       *type;
00040     char       *resultfile;
00041     struct _resultmap *next;
00042 }   _resultmap;
00043 
00044 /*
00045  * Values obtained from pg_config_paths.h and Makefile.  The PG installation
00046  * paths are only used in temp_install mode: we use these strings to find
00047  * out where "make install" will put stuff under the temp_install directory.
00048  * In non-temp_install mode, the only thing we need is the location of psql,
00049  * which we expect to find in psqldir, or in the PATH if psqldir isn't given.
00050  *
00051  * XXX Because pg_regress is not installed in bindir, we can't support
00052  * this for relocatable trees as it is.  --psqldir would need to be
00053  * specified in those cases.
00054  */
00055 char       *bindir = PGBINDIR;
00056 char       *libdir = LIBDIR;
00057 char       *datadir = PGSHAREDIR;
00058 char       *host_platform = HOST_TUPLE;
00059 
00060 #ifndef WIN32_ONLY_COMPILER
00061 static char *makeprog = MAKEPROG;
00062 #endif
00063 
00064 #ifndef WIN32                   /* not used in WIN32 case */
00065 static char *shellprog = SHELLPROG;
00066 #endif
00067 
00068 /*
00069  * On Windows we use -w in diff switches to avoid problems with inconsistent
00070  * newline representation.  The actual result files will generally have
00071  * Windows-style newlines, but the comparison files might or might not.
00072  */
00073 #ifndef WIN32
00074 const char *basic_diff_opts = "";
00075 const char *pretty_diff_opts = "-C3";
00076 #else
00077 const char *basic_diff_opts = "-w";
00078 const char *pretty_diff_opts = "-w -C3";
00079 #endif
00080 
00081 /* options settable from command line */
00082 _stringlist *dblist = NULL;
00083 bool        debug = false;
00084 char       *inputdir = ".";
00085 char       *outputdir = ".";
00086 char       *psqldir = PGBINDIR;
00087 char       *launcher = NULL;
00088 static _stringlist *loadlanguage = NULL;
00089 static _stringlist *loadextension = NULL;
00090 static int  max_connections = 0;
00091 static char *encoding = NULL;
00092 static _stringlist *schedulelist = NULL;
00093 static _stringlist *extra_tests = NULL;
00094 static char *temp_install = NULL;
00095 static char *temp_config = NULL;
00096 static char *top_builddir = NULL;
00097 static bool nolocale = false;
00098 static bool use_existing = false;
00099 static char *hostname = NULL;
00100 static int  port = -1;
00101 static bool port_specified_by_user = false;
00102 static char *dlpath = PKGLIBDIR;
00103 static char *user = NULL;
00104 static _stringlist *extraroles = NULL;
00105 static _stringlist *extra_install = NULL;
00106 
00107 /* internal variables */
00108 static const char *progname;
00109 static char *logfilename;
00110 static FILE *logfile;
00111 static char *difffilename;
00112 
00113 static _resultmap *resultmap = NULL;
00114 
00115 static PID_TYPE postmaster_pid = INVALID_PID;
00116 static bool postmaster_running = false;
00117 
00118 static int  success_count = 0;
00119 static int  fail_count = 0;
00120 static int  fail_ignore_count = 0;
00121 
00122 static bool directory_exists(const char *dir);
00123 static void make_directory(const char *dir);
00124 
00125 static void
00126 header(const char *fmt,...)
00127 /* This extension allows gcc to check the format string for consistency with
00128    the supplied arguments. */
00129 __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
00130 static void
00131 status(const char *fmt,...)
00132 /* This extension allows gcc to check the format string for consistency with
00133    the supplied arguments. */
00134 __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
00135 static void
00136 psql_command(const char *database, const char *query,...)
00137 /* This extension allows gcc to check the format string for consistency with
00138    the supplied arguments. */
00139 __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
00140 
00141 #ifdef WIN32
00142 typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
00143 
00144 /* Windows API define missing from some versions of MingW headers */
00145 #ifndef  DISABLE_MAX_PRIVILEGE
00146 #define DISABLE_MAX_PRIVILEGE   0x1
00147 #endif
00148 #endif
00149 
00150 /*
00151  * allow core files if possible.
00152  */
00153 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
00154 static void
00155 unlimit_core_size(void)
00156 {
00157     struct rlimit lim;
00158 
00159     getrlimit(RLIMIT_CORE, &lim);
00160     if (lim.rlim_max == 0)
00161     {
00162         fprintf(stderr,
00163                 _("%s: could not set core size: disallowed by hard limit\n"),
00164                 progname);
00165         return;
00166     }
00167     else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
00168     {
00169         lim.rlim_cur = lim.rlim_max;
00170         setrlimit(RLIMIT_CORE, &lim);
00171     }
00172 }
00173 #endif
00174 
00175 
00176 /*
00177  * Add an item at the end of a stringlist.
00178  */
00179 void
00180 add_stringlist_item(_stringlist ** listhead, const char *str)
00181 {
00182     _stringlist *newentry = malloc(sizeof(_stringlist));
00183     _stringlist *oldentry;
00184 
00185     newentry->str = strdup(str);
00186     newentry->next = NULL;
00187     if (*listhead == NULL)
00188         *listhead = newentry;
00189     else
00190     {
00191         for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
00192              /* skip */ ;
00193         oldentry->next = newentry;
00194     }
00195 }
00196 
00197 /*
00198  * Free a stringlist.
00199  */
00200 static void
00201 free_stringlist(_stringlist ** listhead)
00202 {
00203     if (listhead == NULL || *listhead == NULL)
00204         return;
00205     if ((*listhead)->next != NULL)
00206         free_stringlist(&((*listhead)->next));
00207     free((*listhead)->str);
00208     free(*listhead);
00209     *listhead = NULL;
00210 }
00211 
00212 /*
00213  * Split a delimited string into a stringlist
00214  */
00215 static void
00216 split_to_stringlist(const char *s, const char *delim, _stringlist ** listhead)
00217 {
00218     char       *sc = strdup(s);
00219     char       *token = strtok(sc, delim);
00220 
00221     while (token)
00222     {
00223         add_stringlist_item(listhead, token);
00224         token = strtok(NULL, delim);
00225     }
00226     free(sc);
00227 }
00228 
00229 /*
00230  * Print a progress banner on stdout.
00231  */
00232 static void
00233 header(const char *fmt,...)
00234 {
00235     char        tmp[64];
00236     va_list     ap;
00237 
00238     va_start(ap, fmt);
00239     vsnprintf(tmp, sizeof(tmp), fmt, ap);
00240     va_end(ap);
00241 
00242     fprintf(stdout, "============== %-38s ==============\n", tmp);
00243     fflush(stdout);
00244 }
00245 
00246 /*
00247  * Print "doing something ..." --- supplied text should not end with newline
00248  */
00249 static void
00250 status(const char *fmt,...)
00251 {
00252     va_list     ap;
00253 
00254     va_start(ap, fmt);
00255     vfprintf(stdout, fmt, ap);
00256     fflush(stdout);
00257     va_end(ap);
00258 
00259     if (logfile)
00260     {
00261         va_start(ap, fmt);
00262         vfprintf(logfile, fmt, ap);
00263         va_end(ap);
00264     }
00265 }
00266 
00267 /*
00268  * Done "doing something ..."
00269  */
00270 static void
00271 status_end(void)
00272 {
00273     fprintf(stdout, "\n");
00274     fflush(stdout);
00275     if (logfile)
00276         fprintf(logfile, "\n");
00277 }
00278 
00279 /*
00280  * shut down temp postmaster
00281  */
00282 static void
00283 stop_postmaster(void)
00284 {
00285     if (postmaster_running)
00286     {
00287         /* We use pg_ctl to issue the kill and wait for stop */
00288         char        buf[MAXPGPATH * 2];
00289         int         r;
00290 
00291         /* On Windows, system() seems not to force fflush, so... */
00292         fflush(stdout);
00293         fflush(stderr);
00294 
00295         snprintf(buf, sizeof(buf),
00296                  SYSTEMQUOTE "\"%s/pg_ctl\" stop -D \"%s/data\" -s -m fast" SYSTEMQUOTE,
00297                  bindir, temp_install);
00298         r = system(buf);
00299         if (r != 0)
00300         {
00301             fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
00302                     progname, r);
00303             _exit(2);           /* not exit(), that could be recursive */
00304         }
00305 
00306         postmaster_running = false;
00307     }
00308 }
00309 
00310 /*
00311  * Check whether string matches pattern
00312  *
00313  * In the original shell script, this function was implemented using expr(1),
00314  * which provides basic regular expressions restricted to match starting at
00315  * the string start (in conventional regex terms, there's an implicit "^"
00316  * at the start of the pattern --- but no implicit "$" at the end).
00317  *
00318  * For now, we only support "." and ".*" as non-literal metacharacters,
00319  * because that's all that anyone has found use for in resultmap.  This
00320  * code could be extended if more functionality is needed.
00321  */
00322 static bool
00323 string_matches_pattern(const char *str, const char *pattern)
00324 {
00325     while (*str && *pattern)
00326     {
00327         if (*pattern == '.' && pattern[1] == '*')
00328         {
00329             pattern += 2;
00330             /* Trailing .* matches everything. */
00331             if (*pattern == '\0')
00332                 return true;
00333 
00334             /*
00335              * Otherwise, scan for a text position at which we can match the
00336              * rest of the pattern.
00337              */
00338             while (*str)
00339             {
00340                 /*
00341                  * Optimization to prevent most recursion: don't recurse
00342                  * unless first pattern char might match this text char.
00343                  */
00344                 if (*str == *pattern || *pattern == '.')
00345                 {
00346                     if (string_matches_pattern(str, pattern))
00347                         return true;
00348                 }
00349 
00350                 str++;
00351             }
00352 
00353             /*
00354              * End of text with no match.
00355              */
00356             return false;
00357         }
00358         else if (*pattern != '.' && *str != *pattern)
00359         {
00360             /*
00361              * Not the single-character wildcard and no explicit match? Then
00362              * time to quit...
00363              */
00364             return false;
00365         }
00366 
00367         str++;
00368         pattern++;
00369     }
00370 
00371     if (*pattern == '\0')
00372         return true;            /* end of pattern, so declare match */
00373 
00374     /* End of input string.  Do we have matching pattern remaining? */
00375     while (*pattern == '.' && pattern[1] == '*')
00376         pattern += 2;
00377     if (*pattern == '\0')
00378         return true;            /* end of pattern, so declare match */
00379 
00380     return false;
00381 }
00382 
00383 /*
00384  * Replace all occurrences of a string in a string with a different string.
00385  * NOTE: Assumes there is enough room in the target buffer!
00386  */
00387 void
00388 replace_string(char *string, char *replace, char *replacement)
00389 {
00390     char       *ptr;
00391 
00392     while ((ptr = strstr(string, replace)) != NULL)
00393     {
00394         char       *dup = strdup(string);
00395 
00396         strlcpy(string, dup, ptr - string + 1);
00397         strcat(string, replacement);
00398         strcat(string, dup + (ptr - string) + strlen(replace));
00399         free(dup);
00400     }
00401 }
00402 
00403 /*
00404  * Convert *.source found in the "source" directory, replacing certain tokens
00405  * in the file contents with their intended values, and put the resulting files
00406  * in the "dest" directory, replacing the ".source" prefix in their names with
00407  * the given suffix.
00408  */
00409 static void
00410 convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, char *suffix)
00411 {
00412     char        testtablespace[MAXPGPATH];
00413     char        indir[MAXPGPATH];
00414     struct stat st;
00415     int         ret;
00416     char      **name;
00417     char      **names;
00418     int         count = 0;
00419 
00420     snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir);
00421 
00422     /* Check that indir actually exists and is a directory */
00423     ret = stat(indir, &st);
00424     if (ret != 0 || !S_ISDIR(st.st_mode))
00425     {
00426         /*
00427          * No warning, to avoid noise in tests that do not have these
00428          * directories; for example, ecpg, contrib and src/pl.
00429          */
00430         return;
00431     }
00432 
00433     names = pgfnames(indir);
00434     if (!names)
00435         /* Error logged in pgfnames */
00436         exit(2);
00437 
00438     snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
00439 
00440 #ifdef WIN32
00441 
00442     /*
00443      * On Windows only, clean out the test tablespace dir, or create it if it
00444      * doesn't exist.  On other platforms we expect the Makefile to take care
00445      * of that.  (We don't migrate that functionality in here because it'd be
00446      * harder to cope with platform-specific issues such as SELinux.)
00447      *
00448      * XXX it would be better if pg_regress.c had nothing at all to do with
00449      * testtablespace, and this were handled by a .BAT file or similar on
00450      * Windows.  See pgsql-hackers discussion of 2008-01-18.
00451      */
00452     if (directory_exists(testtablespace))
00453         rmtree(testtablespace, true);
00454     make_directory(testtablespace);
00455 #endif
00456 
00457     /* finally loop on each file and do the replacement */
00458     for (name = names; *name; name++)
00459     {
00460         char        srcfile[MAXPGPATH];
00461         char        destfile[MAXPGPATH];
00462         char        prefix[MAXPGPATH];
00463         FILE       *infile,
00464                    *outfile;
00465         char        line[1024];
00466 
00467         /* reject filenames not finishing in ".source" */
00468         if (strlen(*name) < 8)
00469             continue;
00470         if (strcmp(*name + strlen(*name) - 7, ".source") != 0)
00471             continue;
00472 
00473         count++;
00474 
00475         /* build the full actual paths to open */
00476         snprintf(prefix, strlen(*name) - 6, "%s", *name);
00477         snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name);
00478         snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir,
00479                  prefix, suffix);
00480 
00481         infile = fopen(srcfile, "r");
00482         if (!infile)
00483         {
00484             fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
00485                     progname, srcfile, strerror(errno));
00486             exit(2);
00487         }
00488         outfile = fopen(destfile, "w");
00489         if (!outfile)
00490         {
00491             fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
00492                     progname, destfile, strerror(errno));
00493             exit(2);
00494         }
00495         while (fgets(line, sizeof(line), infile))
00496         {
00497             replace_string(line, "@[email protected]", inputdir);
00498             replace_string(line, "@[email protected]", outputdir);
00499             replace_string(line, "@[email protected]", testtablespace);
00500             replace_string(line, "@[email protected]", dlpath);
00501             replace_string(line, "@[email protected]", DLSUFFIX);
00502             fputs(line, outfile);
00503         }
00504         fclose(infile);
00505         fclose(outfile);
00506     }
00507 
00508     /*
00509      * If we didn't process any files, complain because it probably means
00510      * somebody neglected to pass the needed --inputdir argument.
00511      */
00512     if (count <= 0)
00513     {
00514         fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"),
00515                 progname, indir);
00516         exit(2);
00517     }
00518 
00519     pgfnames_cleanup(names);
00520 }
00521 
00522 /* Create the .sql and .out files from the .source files, if any */
00523 static void
00524 convert_sourcefiles(void)
00525 {
00526     convert_sourcefiles_in("input", inputdir, "sql", "sql");
00527     convert_sourcefiles_in("output", outputdir, "expected", "out");
00528 }
00529 
00530 /*
00531  * Scan resultmap file to find which platform-specific expected files to use.
00532  *
00533  * The format of each line of the file is
00534  *         testname/hostplatformpattern=substitutefile
00535  * where the hostplatformpattern is evaluated per the rules of expr(1),
00536  * namely, it is a standard regular expression with an implicit ^ at the start.
00537  * (We currently support only a very limited subset of regular expressions,
00538  * see string_matches_pattern() above.)  What hostplatformpattern will be
00539  * matched against is the config.guess output.  (In the shell-script version,
00540  * we also provided an indication of whether gcc or another compiler was in
00541  * use, but that facility isn't used anymore.)
00542  */
00543 static void
00544 load_resultmap(void)
00545 {
00546     char        buf[MAXPGPATH];
00547     FILE       *f;
00548 
00549     /* scan the file ... */
00550     snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
00551     f = fopen(buf, "r");
00552     if (!f)
00553     {
00554         /* OK if it doesn't exist, else complain */
00555         if (errno == ENOENT)
00556             return;
00557         fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
00558                 progname, buf, strerror(errno));
00559         exit(2);
00560     }
00561 
00562     while (fgets(buf, sizeof(buf), f))
00563     {
00564         char       *platform;
00565         char       *file_type;
00566         char       *expected;
00567         int         i;
00568 
00569         /* strip trailing whitespace, especially the newline */
00570         i = strlen(buf);
00571         while (i > 0 && isspace((unsigned char) buf[i - 1]))
00572             buf[--i] = '\0';
00573 
00574         /* parse out the line fields */
00575         file_type = strchr(buf, ':');
00576         if (!file_type)
00577         {
00578             fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
00579                     buf);
00580             exit(2);
00581         }
00582         *file_type++ = '\0';
00583 
00584         platform = strchr(file_type, ':');
00585         if (!platform)
00586         {
00587             fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
00588                     buf);
00589             exit(2);
00590         }
00591         *platform++ = '\0';
00592         expected = strchr(platform, '=');
00593         if (!expected)
00594         {
00595             fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
00596                     buf);
00597             exit(2);
00598         }
00599         *expected++ = '\0';
00600 
00601         /*
00602          * if it's for current platform, save it in resultmap list. Note: by
00603          * adding at the front of the list, we ensure that in ambiguous cases,
00604          * the last match in the resultmap file is used. This mimics the
00605          * behavior of the old shell script.
00606          */
00607         if (string_matches_pattern(host_platform, platform))
00608         {
00609             _resultmap *entry = malloc(sizeof(_resultmap));
00610 
00611             entry->test = strdup(buf);
00612             entry->type = strdup(file_type);
00613             entry->resultfile = strdup(expected);
00614             entry->next = resultmap;
00615             resultmap = entry;
00616         }
00617     }
00618     fclose(f);
00619 }
00620 
00621 /*
00622  * Check in resultmap if we should be looking at a different file
00623  */
00624 static
00625 const char *
00626 get_expectfile(const char *testname, const char *file)
00627 {
00628     char       *file_type;
00629     _resultmap *rm;
00630 
00631     /*
00632      * Determine the file type from the file name. This is just what is
00633      * following the last dot in the file name.
00634      */
00635     if (!file || !(file_type = strrchr(file, '.')))
00636         return NULL;
00637 
00638     file_type++;
00639 
00640     for (rm = resultmap; rm != NULL; rm = rm->next)
00641     {
00642         if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
00643         {
00644             return rm->resultfile;
00645         }
00646     }
00647 
00648     return NULL;
00649 }
00650 
00651 /*
00652  * Handy subroutine for setting an environment variable "var" to "val"
00653  */
00654 static void
00655 doputenv(const char *var, const char *val)
00656 {
00657     char       *s = malloc(strlen(var) + strlen(val) + 2);
00658 
00659     sprintf(s, "%s=%s", var, val);
00660     putenv(s);
00661 }
00662 
00663 /*
00664  * Set the environment variable "pathname", prepending "addval" to its
00665  * old value (if any).
00666  */
00667 static void
00668 add_to_path(const char *pathname, char separator, const char *addval)
00669 {
00670     char       *oldval = getenv(pathname);
00671     char       *newval;
00672 
00673     if (!oldval || !oldval[0])
00674     {
00675         /* no previous value */
00676         newval = malloc(strlen(pathname) + strlen(addval) + 2);
00677         sprintf(newval, "%s=%s", pathname, addval);
00678     }
00679     else
00680     {
00681         newval = malloc(strlen(pathname) + strlen(addval) + strlen(oldval) + 3);
00682         sprintf(newval, "%s=%s%c%s", pathname, addval, separator, oldval);
00683     }
00684     putenv(newval);
00685 }
00686 
00687 /*
00688  * Prepare environment variables for running regression tests
00689  */
00690 static void
00691 initialize_environment(void)
00692 {
00693     char       *tmp;
00694 
00695     putenv("PGAPPNAME=pg_regress");
00696 
00697     if (nolocale)
00698     {
00699         /*
00700          * Clear out any non-C locale settings
00701          */
00702         unsetenv("LC_COLLATE");
00703         unsetenv("LC_CTYPE");
00704         unsetenv("LC_MONETARY");
00705         unsetenv("LC_NUMERIC");
00706         unsetenv("LC_TIME");
00707         unsetenv("LANG");
00708         /* On Windows the default locale cannot be English, so force it */
00709 #if defined(WIN32) || defined(__CYGWIN__)
00710         putenv("LANG=en");
00711 #endif
00712     }
00713 
00714     /*
00715      * Set translation-related settings to English; otherwise psql will
00716      * produce translated messages and produce diffs.  (XXX If we ever support
00717      * translation of pg_regress, this needs to be moved elsewhere, where psql
00718      * is actually called.)
00719      */
00720     unsetenv("LANGUAGE");
00721     unsetenv("LC_ALL");
00722     putenv("LC_MESSAGES=C");
00723 
00724     /*
00725      * Set encoding as requested
00726      */
00727     if (encoding)
00728         doputenv("PGCLIENTENCODING", encoding);
00729     else
00730         unsetenv("PGCLIENTENCODING");
00731 
00732     /*
00733      * Set timezone and datestyle for datetime-related tests
00734      */
00735     putenv("PGTZ=PST8PDT");
00736     putenv("PGDATESTYLE=Postgres, MDY");
00737 
00738     /*
00739      * Likewise set intervalstyle to ensure consistent results.  This is a bit
00740      * more painful because we must use PGOPTIONS, and we want to preserve the
00741      * user's ability to set other variables through that.
00742      */
00743     {
00744         const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
00745         const char *old_pgoptions = getenv("PGOPTIONS");
00746         char       *new_pgoptions;
00747 
00748         if (!old_pgoptions)
00749             old_pgoptions = "";
00750         new_pgoptions = malloc(strlen(old_pgoptions) + strlen(my_pgoptions) + 12);
00751         sprintf(new_pgoptions, "PGOPTIONS=%s %s", old_pgoptions, my_pgoptions);
00752         putenv(new_pgoptions);
00753     }
00754 
00755     if (temp_install)
00756     {
00757         /*
00758          * Clear out any environment vars that might cause psql to connect to
00759          * the wrong postmaster, or otherwise behave in nondefault ways. (Note
00760          * we also use psql's -X switch consistently, so that ~/.psqlrc files
00761          * won't mess things up.)  Also, set PGPORT to the temp port, and set
00762          * or unset PGHOST depending on whether we are using TCP or Unix
00763          * sockets.
00764          */
00765         unsetenv("PGDATABASE");
00766         unsetenv("PGUSER");
00767         unsetenv("PGSERVICE");
00768         unsetenv("PGSSLMODE");
00769         unsetenv("PGREQUIRESSL");
00770         unsetenv("PGCONNECT_TIMEOUT");
00771         unsetenv("PGDATA");
00772         if (hostname != NULL)
00773             doputenv("PGHOST", hostname);
00774         else
00775             unsetenv("PGHOST");
00776         unsetenv("PGHOSTADDR");
00777         if (port != -1)
00778         {
00779             char        s[16];
00780 
00781             sprintf(s, "%d", port);
00782             doputenv("PGPORT", s);
00783         }
00784 
00785         /*
00786          * GNU make stores some flags in the MAKEFLAGS environment variable to
00787          * pass arguments to its own children.  If we are invoked by make,
00788          * that causes the make invoked by us to think its part of the make
00789          * task invoking us, and so it tries to communicate with the toplevel
00790          * make.  Which fails.
00791          *
00792          * Unset the variable to protect against such problems.  We also reset
00793          * MAKELEVEL to be certain the child doesn't notice the make above us.
00794          */
00795         unsetenv("MAKEFLAGS");
00796         unsetenv("MAKELEVEL");
00797 
00798         /*
00799          * Adjust path variables to point into the temp-install tree
00800          */
00801         tmp = malloc(strlen(temp_install) + 32 + strlen(bindir));
00802         sprintf(tmp, "%s/install/%s", temp_install, bindir);
00803         bindir = tmp;
00804 
00805         tmp = malloc(strlen(temp_install) + 32 + strlen(libdir));
00806         sprintf(tmp, "%s/install/%s", temp_install, libdir);
00807         libdir = tmp;
00808 
00809         tmp = malloc(strlen(temp_install) + 32 + strlen(datadir));
00810         sprintf(tmp, "%s/install/%s", temp_install, datadir);
00811         datadir = tmp;
00812 
00813         /* psql will be installed into temp-install bindir */
00814         psqldir = bindir;
00815 
00816         /*
00817          * Set up shared library paths to include the temp install.
00818          *
00819          * LD_LIBRARY_PATH covers many platforms.  DYLD_LIBRARY_PATH works on
00820          * Darwin, and maybe other Mach-based systems.  LIBPATH is for AIX.
00821          * Windows needs shared libraries in PATH (only those linked into
00822          * executables, not dlopen'ed ones). Feel free to account for others
00823          * as well.
00824          */
00825         add_to_path("LD_LIBRARY_PATH", ':', libdir);
00826         add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
00827         add_to_path("LIBPATH", ':', libdir);
00828 #if defined(WIN32)
00829         add_to_path("PATH", ';', libdir);
00830 #elif defined(__CYGWIN__)
00831         add_to_path("PATH", ':', libdir);
00832 #endif
00833     }
00834     else
00835     {
00836         const char *pghost;
00837         const char *pgport;
00838 
00839         /*
00840          * When testing an existing install, we honor existing environment
00841          * variables, except if they're overridden by command line options.
00842          */
00843         if (hostname != NULL)
00844         {
00845             doputenv("PGHOST", hostname);
00846             unsetenv("PGHOSTADDR");
00847         }
00848         if (port != -1)
00849         {
00850             char        s[16];
00851 
00852             sprintf(s, "%d", port);
00853             doputenv("PGPORT", s);
00854         }
00855         if (user != NULL)
00856             doputenv("PGUSER", user);
00857 
00858         /*
00859          * Report what we're connecting to
00860          */
00861         pghost = getenv("PGHOST");
00862         pgport = getenv("PGPORT");
00863 #ifndef HAVE_UNIX_SOCKETS
00864         if (!pghost)
00865             pghost = "localhost";
00866 #endif
00867 
00868         if (pghost && pgport)
00869             printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport);
00870         if (pghost && !pgport)
00871             printf(_("(using postmaster on %s, default port)\n"), pghost);
00872         if (!pghost && pgport)
00873             printf(_("(using postmaster on Unix socket, port %s)\n"), pgport);
00874         if (!pghost && !pgport)
00875             printf(_("(using postmaster on Unix socket, default port)\n"));
00876     }
00877 
00878     convert_sourcefiles();
00879     load_resultmap();
00880 }
00881 
00882 /*
00883  * Issue a command via psql, connecting to the specified database
00884  *
00885  * Since we use system(), this doesn't return until the operation finishes
00886  */
00887 static void
00888 psql_command(const char *database, const char *query,...)
00889 {
00890     char        query_formatted[1024];
00891     char        query_escaped[2048];
00892     char        psql_cmd[MAXPGPATH + 2048];
00893     va_list     args;
00894     char       *s;
00895     char       *d;
00896 
00897     /* Generate the query with insertion of sprintf arguments */
00898     va_start(args, query);
00899     vsnprintf(query_formatted, sizeof(query_formatted), query, args);
00900     va_end(args);
00901 
00902     /* Now escape any shell double-quote metacharacters */
00903     d = query_escaped;
00904     for (s = query_formatted; *s; s++)
00905     {
00906         if (strchr("\\\"$`", *s))
00907             *d++ = '\\';
00908         *d++ = *s;
00909     }
00910     *d = '\0';
00911 
00912     /* And now we can build and execute the shell command */
00913     snprintf(psql_cmd, sizeof(psql_cmd),
00914              SYSTEMQUOTE "\"%s%spsql\" -X -c \"%s\" \"%s\"" SYSTEMQUOTE,
00915              psqldir ? psqldir : "",
00916              psqldir ? "/" : "",
00917              query_escaped,
00918              database);
00919 
00920     if (system(psql_cmd) != 0)
00921     {
00922         /* psql probably already reported the error */
00923         fprintf(stderr, _("command failed: %s\n"), psql_cmd);
00924         exit(2);
00925     }
00926 }
00927 
00928 /*
00929  * Spawn a process to execute the given shell command; don't wait for it
00930  *
00931  * Returns the process ID (or HANDLE) so we can wait for it later
00932  */
00933 PID_TYPE
00934 spawn_process(const char *cmdline)
00935 {
00936 #ifndef WIN32
00937     pid_t       pid;
00938 
00939     /*
00940      * Must flush I/O buffers before fork.  Ideally we'd use fflush(NULL) here
00941      * ... does anyone still care about systems where that doesn't work?
00942      */
00943     fflush(stdout);
00944     fflush(stderr);
00945     if (logfile)
00946         fflush(logfile);
00947 
00948     pid = fork();
00949     if (pid == -1)
00950     {
00951         fprintf(stderr, _("%s: could not fork: %s\n"),
00952                 progname, strerror(errno));
00953         exit(2);
00954     }
00955     if (pid == 0)
00956     {
00957         /*
00958          * In child
00959          *
00960          * Instead of using system(), exec the shell directly, and tell it to
00961          * "exec" the command too.  This saves two useless processes per
00962          * parallel test case.
00963          */
00964         char       *cmdline2 = malloc(strlen(cmdline) + 6);
00965 
00966         sprintf(cmdline2, "exec %s", cmdline);
00967         execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
00968         fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
00969                 progname, shellprog, strerror(errno));
00970         _exit(1);               /* not exit() here... */
00971     }
00972     /* in parent */
00973     return pid;
00974 #else
00975     char       *cmdline2;
00976     BOOL        b;
00977     STARTUPINFO si;
00978     PROCESS_INFORMATION pi;
00979     HANDLE      origToken;
00980     HANDLE      restrictedToken;
00981     SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
00982     SID_AND_ATTRIBUTES dropSids[2];
00983     __CreateRestrictedToken _CreateRestrictedToken = NULL;
00984     HANDLE      Advapi32Handle;
00985 
00986     ZeroMemory(&si, sizeof(si));
00987     si.cb = sizeof(si);
00988 
00989     Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
00990     if (Advapi32Handle != NULL)
00991     {
00992         _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
00993     }
00994 
00995     if (_CreateRestrictedToken == NULL)
00996     {
00997         if (Advapi32Handle != NULL)
00998             FreeLibrary(Advapi32Handle);
00999         fprintf(stderr, _("%s: cannot create restricted tokens on this platform\n"),
01000                 progname);
01001         exit(2);
01002     }
01003 
01004     /* Open the current token to use as base for the restricted one */
01005     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
01006     {
01007         fprintf(stderr, _("could not open process token: error code %lu\n"),
01008                 GetLastError());
01009         exit(2);
01010     }
01011 
01012     /* Allocate list of SIDs to remove */
01013     ZeroMemory(&dropSids, sizeof(dropSids));
01014     if (!AllocateAndInitializeSid(&NtAuthority, 2,
01015                                   SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &dropSids[0].Sid) ||
01016         !AllocateAndInitializeSid(&NtAuthority, 2,
01017                                   SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &dropSids[1].Sid))
01018     {
01019         fprintf(stderr, _("could not allocate SIDs: error code %lu\n"), GetLastError());
01020         exit(2);
01021     }
01022 
01023     b = _CreateRestrictedToken(origToken,
01024                                DISABLE_MAX_PRIVILEGE,
01025                                sizeof(dropSids) / sizeof(dropSids[0]),
01026                                dropSids,
01027                                0, NULL,
01028                                0, NULL,
01029                                &restrictedToken);
01030 
01031     FreeSid(dropSids[1].Sid);
01032     FreeSid(dropSids[0].Sid);
01033     CloseHandle(origToken);
01034     FreeLibrary(Advapi32Handle);
01035 
01036     if (!b)
01037     {
01038         fprintf(stderr, _("could not create restricted token: error code %lu\n"),
01039                 GetLastError());
01040         exit(2);
01041     }
01042 
01043     cmdline2 = malloc(strlen(cmdline) + 8);
01044     sprintf(cmdline2, "cmd /c %s", cmdline);
01045 
01046 #ifndef __CYGWIN__
01047     AddUserToTokenDacl(restrictedToken);
01048 #endif
01049 
01050     if (!CreateProcessAsUser(restrictedToken,
01051                              NULL,
01052                              cmdline2,
01053                              NULL,
01054                              NULL,
01055                              TRUE,
01056                              CREATE_SUSPENDED,
01057                              NULL,
01058                              NULL,
01059                              &si,
01060                              &pi))
01061     {
01062         fprintf(stderr, _("could not start process for \"%s\": error code %lu\n"),
01063                 cmdline2, GetLastError());
01064         exit(2);
01065     }
01066 
01067     free(cmdline2);
01068 
01069     ResumeThread(pi.hThread);
01070     CloseHandle(pi.hThread);
01071     return pi.hProcess;
01072 #endif
01073 }
01074 
01075 /*
01076  * Count bytes in file
01077  */
01078 static long
01079 file_size(const char *file)
01080 {
01081     long        r;
01082     FILE       *f = fopen(file, "r");
01083 
01084     if (!f)
01085     {
01086         fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
01087                 progname, file, strerror(errno));
01088         return -1;
01089     }
01090     fseek(f, 0, SEEK_END);
01091     r = ftell(f);
01092     fclose(f);
01093     return r;
01094 }
01095 
01096 /*
01097  * Count lines in file
01098  */
01099 static int
01100 file_line_count(const char *file)
01101 {
01102     int         c;
01103     int         l = 0;
01104     FILE       *f = fopen(file, "r");
01105 
01106     if (!f)
01107     {
01108         fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
01109                 progname, file, strerror(errno));
01110         return -1;
01111     }
01112     while ((c = fgetc(f)) != EOF)
01113     {
01114         if (c == '\n')
01115             l++;
01116     }
01117     fclose(f);
01118     return l;
01119 }
01120 
01121 bool
01122 file_exists(const char *file)
01123 {
01124     FILE       *f = fopen(file, "r");
01125 
01126     if (!f)
01127         return false;
01128     fclose(f);
01129     return true;
01130 }
01131 
01132 static bool
01133 directory_exists(const char *dir)
01134 {
01135     struct stat st;
01136 
01137     if (stat(dir, &st) != 0)
01138         return false;
01139     if (S_ISDIR(st.st_mode))
01140         return true;
01141     return false;
01142 }
01143 
01144 /* Create a directory */
01145 static void
01146 make_directory(const char *dir)
01147 {
01148     if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
01149     {
01150         fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
01151                 progname, dir, strerror(errno));
01152         exit(2);
01153     }
01154 }
01155 
01156 /*
01157  * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
01158  */
01159 static char *
01160 get_alternative_expectfile(const char *expectfile, int i)
01161 {
01162     char       *last_dot;
01163     int         ssize = strlen(expectfile) + 2 + 1;
01164     char       *tmp = (char *) malloc(ssize);
01165     char       *s = (char *) malloc(ssize);
01166 
01167     strcpy(tmp, expectfile);
01168     last_dot = strrchr(tmp, '.');
01169     if (!last_dot)
01170     {
01171         free(tmp);
01172         free(s);
01173         return NULL;
01174     }
01175     *last_dot = '\0';
01176     snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
01177     free(tmp);
01178     return s;
01179 }
01180 
01181 /*
01182  * Run a "diff" command and also check that it didn't crash
01183  */
01184 static int
01185 run_diff(const char *cmd, const char *filename)
01186 {
01187     int         r;
01188 
01189     r = system(cmd);
01190     if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
01191     {
01192         fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
01193         exit(2);
01194     }
01195 #ifdef WIN32
01196 
01197     /*
01198      * On WIN32, if the 'diff' command cannot be found, system() returns 1,
01199      * but produces nothing to stdout, so we check for that here.
01200      */
01201     if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
01202     {
01203         fprintf(stderr, _("diff command not found: %s\n"), cmd);
01204         exit(2);
01205     }
01206 #endif
01207 
01208     return WEXITSTATUS(r);
01209 }
01210 
01211 /*
01212  * Check the actual result file for the given test against expected results
01213  *
01214  * Returns true if different (failure), false if correct match found.
01215  * In the true case, the diff is appended to the diffs file.
01216  */
01217 static bool
01218 results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
01219 {
01220     char        expectfile[MAXPGPATH];
01221     char        diff[MAXPGPATH];
01222     char        cmd[MAXPGPATH * 3];
01223     char        best_expect_file[MAXPGPATH];
01224     FILE       *difffile;
01225     int         best_line_count;
01226     int         i;
01227     int         l;
01228     const char *platform_expectfile;
01229 
01230     /*
01231      * We can pass either the resultsfile or the expectfile, they should have
01232      * the same type (filename.type) anyway.
01233      */
01234     platform_expectfile = get_expectfile(testname, resultsfile);
01235 
01236     strcpy(expectfile, default_expectfile);
01237     if (platform_expectfile)
01238     {
01239         /*
01240          * Replace everything afer the last slash in expectfile with what the
01241          * platform_expectfile contains.
01242          */
01243         char       *p = strrchr(expectfile, '/');
01244 
01245         if (p)
01246             strcpy(++p, platform_expectfile);
01247     }
01248 
01249     /* Name to use for temporary diff file */
01250     snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
01251 
01252     /* OK, run the diff */
01253     snprintf(cmd, sizeof(cmd),
01254              SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
01255              basic_diff_opts, expectfile, resultsfile, diff);
01256 
01257     /* Is the diff file empty? */
01258     if (run_diff(cmd, diff) == 0)
01259     {
01260         unlink(diff);
01261         return false;
01262     }
01263 
01264     /* There may be secondary comparison files that match better */
01265     best_line_count = file_line_count(diff);
01266     strcpy(best_expect_file, expectfile);
01267 
01268     for (i = 0; i <= 9; i++)
01269     {
01270         char       *alt_expectfile;
01271 
01272         alt_expectfile = get_alternative_expectfile(expectfile, i);
01273         if (!file_exists(alt_expectfile))
01274             continue;
01275 
01276         snprintf(cmd, sizeof(cmd),
01277                  SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
01278                  basic_diff_opts, alt_expectfile, resultsfile, diff);
01279 
01280         if (run_diff(cmd, diff) == 0)
01281         {
01282             unlink(diff);
01283             return false;
01284         }
01285 
01286         l = file_line_count(diff);
01287         if (l < best_line_count)
01288         {
01289             /* This diff was a better match than the last one */
01290             best_line_count = l;
01291             strcpy(best_expect_file, alt_expectfile);
01292         }
01293         free(alt_expectfile);
01294     }
01295 
01296     /*
01297      * fall back on the canonical results file if we haven't tried it yet and
01298      * haven't found a complete match yet.
01299      */
01300 
01301     if (platform_expectfile)
01302     {
01303         snprintf(cmd, sizeof(cmd),
01304                  SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
01305                  basic_diff_opts, default_expectfile, resultsfile, diff);
01306 
01307         if (run_diff(cmd, diff) == 0)
01308         {
01309             /* No diff = no changes = good */
01310             unlink(diff);
01311             return false;
01312         }
01313 
01314         l = file_line_count(diff);
01315         if (l < best_line_count)
01316         {
01317             /* This diff was a better match than the last one */
01318             best_line_count = l;
01319             strcpy(best_expect_file, default_expectfile);
01320         }
01321     }
01322 
01323     /*
01324      * Use the best comparison file to generate the "pretty" diff, which we
01325      * append to the diffs summary file.
01326      */
01327     snprintf(cmd, sizeof(cmd),
01328              SYSTEMQUOTE "diff %s \"%s\" \"%s\" >> \"%s\"" SYSTEMQUOTE,
01329              pretty_diff_opts, best_expect_file, resultsfile, difffilename);
01330     run_diff(cmd, difffilename);
01331 
01332     /* And append a separator */
01333     difffile = fopen(difffilename, "a");
01334     if (difffile)
01335     {
01336         fprintf(difffile,
01337                 "\n======================================================================\n\n");
01338         fclose(difffile);
01339     }
01340 
01341     unlink(diff);
01342     return true;
01343 }
01344 
01345 /*
01346  * Wait for specified subprocesses to finish, and return their exit
01347  * statuses into statuses[]
01348  *
01349  * If names isn't NULL, print each subprocess's name as it finishes
01350  *
01351  * Note: it's OK to scribble on the pids array, but not on the names array
01352  */
01353 static void
01354 wait_for_tests(PID_TYPE * pids, int *statuses, char **names, int num_tests)
01355 {
01356     int         tests_left;
01357     int         i;
01358 
01359 #ifdef WIN32
01360     PID_TYPE   *active_pids = malloc(num_tests * sizeof(PID_TYPE));
01361 
01362     memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
01363 #endif
01364 
01365     tests_left = num_tests;
01366     while (tests_left > 0)
01367     {
01368         PID_TYPE    p;
01369 
01370 #ifndef WIN32
01371         int         exit_status;
01372 
01373         p = wait(&exit_status);
01374 
01375         if (p == INVALID_PID)
01376         {
01377             fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
01378                     strerror(errno));
01379             exit(2);
01380         }
01381 #else
01382         DWORD       exit_status;
01383         int         r;
01384 
01385         r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
01386         if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
01387         {
01388             fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"),
01389                     GetLastError());
01390             exit(2);
01391         }
01392         p = active_pids[r - WAIT_OBJECT_0];
01393         /* compact the active_pids array */
01394         active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
01395 #endif   /* WIN32 */
01396 
01397         for (i = 0; i < num_tests; i++)
01398         {
01399             if (p == pids[i])
01400             {
01401 #ifdef WIN32
01402                 GetExitCodeProcess(pids[i], &exit_status);
01403                 CloseHandle(pids[i]);
01404 #endif
01405                 pids[i] = INVALID_PID;
01406                 statuses[i] = (int) exit_status;
01407                 if (names)
01408                     status(" %s", names[i]);
01409                 tests_left--;
01410                 break;
01411             }
01412         }
01413     }
01414 
01415 #ifdef WIN32
01416     free(active_pids);
01417 #endif
01418 }
01419 
01420 /*
01421  * report nonzero exit code from a test process
01422  */
01423 static void
01424 log_child_failure(int exitstatus)
01425 {
01426     if (WIFEXITED(exitstatus))
01427         status(_(" (test process exited with exit code %d)"),
01428                WEXITSTATUS(exitstatus));
01429     else if (WIFSIGNALED(exitstatus))
01430     {
01431 #if defined(WIN32)
01432         status(_(" (test process was terminated by exception 0x%X)"),
01433                WTERMSIG(exitstatus));
01434 #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
01435         status(_(" (test process was terminated by signal %d: %s)"),
01436                WTERMSIG(exitstatus),
01437                WTERMSIG(exitstatus) < NSIG ?
01438                sys_siglist[WTERMSIG(exitstatus)] : "(unknown))");
01439 #else
01440         status(_(" (test process was terminated by signal %d)"),
01441                WTERMSIG(exitstatus));
01442 #endif
01443     }
01444     else
01445         status(_(" (test process exited with unrecognized status %d)"),
01446                exitstatus);
01447 }
01448 
01449 /*
01450  * Run all the tests specified in one schedule file
01451  */
01452 static void
01453 run_schedule(const char *schedule, test_function tfunc)
01454 {
01455 #define MAX_PARALLEL_TESTS 100
01456     char       *tests[MAX_PARALLEL_TESTS];
01457     _stringlist *resultfiles[MAX_PARALLEL_TESTS];
01458     _stringlist *expectfiles[MAX_PARALLEL_TESTS];
01459     _stringlist *tags[MAX_PARALLEL_TESTS];
01460     PID_TYPE    pids[MAX_PARALLEL_TESTS];
01461     int         statuses[MAX_PARALLEL_TESTS];
01462     _stringlist *ignorelist = NULL;
01463     char        scbuf[1024];
01464     FILE       *scf;
01465     int         line_num = 0;
01466 
01467     memset(resultfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
01468     memset(expectfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
01469     memset(tags, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
01470 
01471     scf = fopen(schedule, "r");
01472     if (!scf)
01473     {
01474         fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
01475                 progname, schedule, strerror(errno));
01476         exit(2);
01477     }
01478 
01479     while (fgets(scbuf, sizeof(scbuf), scf))
01480     {
01481         char       *test = NULL;
01482         char       *c;
01483         int         num_tests;
01484         bool        inword;
01485         int         i;
01486 
01487         line_num++;
01488 
01489         for (i = 0; i < MAX_PARALLEL_TESTS; i++)
01490         {
01491             if (resultfiles[i] == NULL)
01492                 break;
01493             free_stringlist(&resultfiles[i]);
01494             free_stringlist(&expectfiles[i]);
01495             free_stringlist(&tags[i]);
01496         }
01497 
01498         /* strip trailing whitespace, especially the newline */
01499         i = strlen(scbuf);
01500         while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
01501             scbuf[--i] = '\0';
01502 
01503         if (scbuf[0] == '\0' || scbuf[0] == '#')
01504             continue;
01505         if (strncmp(scbuf, "test: ", 6) == 0)
01506             test = scbuf + 6;
01507         else if (strncmp(scbuf, "ignore: ", 8) == 0)
01508         {
01509             c = scbuf + 8;
01510             while (*c && isspace((unsigned char) *c))
01511                 c++;
01512             add_stringlist_item(&ignorelist, c);
01513 
01514             /*
01515              * Note: ignore: lines do not run the test, they just say that
01516              * failure of this test when run later on is to be ignored. A bit
01517              * odd but that's how the shell-script version did it.
01518              */
01519             continue;
01520         }
01521         else
01522         {
01523             fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
01524                     schedule, line_num, scbuf);
01525             exit(2);
01526         }
01527 
01528         num_tests = 0;
01529         inword = false;
01530         for (c = test; *c; c++)
01531         {
01532             if (isspace((unsigned char) *c))
01533             {
01534                 *c = '\0';
01535                 inword = false;
01536             }
01537             else if (!inword)
01538             {
01539                 if (num_tests >= MAX_PARALLEL_TESTS)
01540                 {
01541                     /* can't print scbuf here, it's already been trashed */
01542                     fprintf(stderr, _("too many parallel tests in schedule file \"%s\", line %d\n"),
01543                             schedule, line_num);
01544                     exit(2);
01545                 }
01546                 tests[num_tests] = c;
01547                 num_tests++;
01548                 inword = true;
01549             }
01550         }
01551 
01552         if (num_tests == 0)
01553         {
01554             fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
01555                     schedule, line_num, scbuf);
01556             exit(2);
01557         }
01558 
01559         if (num_tests == 1)
01560         {
01561             status(_("test %-24s ... "), tests[0]);
01562             pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
01563             wait_for_tests(pids, statuses, NULL, 1);
01564             /* status line is finished below */
01565         }
01566         else if (max_connections > 0 && max_connections < num_tests)
01567         {
01568             int         oldest = 0;
01569 
01570             status(_("parallel group (%d tests, in groups of %d): "),
01571                    num_tests, max_connections);
01572             for (i = 0; i < num_tests; i++)
01573             {
01574                 if (i - oldest >= max_connections)
01575                 {
01576                     wait_for_tests(pids + oldest, statuses + oldest,
01577                                    tests + oldest, i - oldest);
01578                     oldest = i;
01579                 }
01580                 pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
01581             }
01582             wait_for_tests(pids + oldest, statuses + oldest,
01583                            tests + oldest, i - oldest);
01584             status_end();
01585         }
01586         else
01587         {
01588             status(_("parallel group (%d tests): "), num_tests);
01589             for (i = 0; i < num_tests; i++)
01590             {
01591                 pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
01592             }
01593             wait_for_tests(pids, statuses, tests, num_tests);
01594             status_end();
01595         }
01596 
01597         /* Check results for all tests */
01598         for (i = 0; i < num_tests; i++)
01599         {
01600             _stringlist *rl,
01601                        *el,
01602                        *tl;
01603             bool        differ = false;
01604 
01605             if (num_tests > 1)
01606                 status(_("     %-24s ... "), tests[i]);
01607 
01608             /*
01609              * Advance over all three lists simultaneously.
01610              *
01611              * Compare resultfiles[j] with expectfiles[j] always. Tags are
01612              * optional but if there are tags, the tag list has the same
01613              * length as the other two lists.
01614              */
01615             for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
01616                  rl != NULL;    /* rl and el have the same length */
01617                  rl = rl->next, el = el->next)
01618             {
01619                 bool        newdiff;
01620 
01621                 if (tl)
01622                     tl = tl->next;      /* tl has the same length as rl and el
01623                                          * if it exists */
01624 
01625                 newdiff = results_differ(tests[i], rl->str, el->str);
01626                 if (newdiff && tl)
01627                 {
01628                     printf("%s ", tl->str);
01629                 }
01630                 differ |= newdiff;
01631             }
01632 
01633             if (differ)
01634             {
01635                 bool        ignore = false;
01636                 _stringlist *sl;
01637 
01638                 for (sl = ignorelist; sl != NULL; sl = sl->next)
01639                 {
01640                     if (strcmp(tests[i], sl->str) == 0)
01641                     {
01642                         ignore = true;
01643                         break;
01644                     }
01645                 }
01646                 if (ignore)
01647                 {
01648                     status(_("failed (ignored)"));
01649                     fail_ignore_count++;
01650                 }
01651                 else
01652                 {
01653                     status(_("FAILED"));
01654                     fail_count++;
01655                 }
01656             }
01657             else
01658             {
01659                 status(_("ok"));
01660                 success_count++;
01661             }
01662 
01663             if (statuses[i] != 0)
01664                 log_child_failure(statuses[i]);
01665 
01666             status_end();
01667         }
01668     }
01669 
01670     fclose(scf);
01671 }
01672 
01673 /*
01674  * Run a single test
01675  */
01676 static void
01677 run_single_test(const char *test, test_function tfunc)
01678 {
01679     PID_TYPE    pid;
01680     int         exit_status;
01681     _stringlist *resultfiles = NULL;
01682     _stringlist *expectfiles = NULL;
01683     _stringlist *tags = NULL;
01684     _stringlist *rl,
01685                *el,
01686                *tl;
01687     bool        differ = false;
01688 
01689     status(_("test %-24s ... "), test);
01690     pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
01691     wait_for_tests(&pid, &exit_status, NULL, 1);
01692 
01693     /*
01694      * Advance over all three lists simultaneously.
01695      *
01696      * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
01697      * but if there are tags, the tag list has the same length as the other
01698      * two lists.
01699      */
01700     for (rl = resultfiles, el = expectfiles, tl = tags;
01701          rl != NULL;            /* rl and el have the same length */
01702          rl = rl->next, el = el->next)
01703     {
01704         bool        newdiff;
01705 
01706         if (tl)
01707             tl = tl->next;      /* tl has the same length as rl and el if it
01708                                  * exists */
01709 
01710         newdiff = results_differ(test, rl->str, el->str);
01711         if (newdiff && tl)
01712         {
01713             printf("%s ", tl->str);
01714         }
01715         differ |= newdiff;
01716     }
01717 
01718     if (differ)
01719     {
01720         status(_("FAILED"));
01721         fail_count++;
01722     }
01723     else
01724     {
01725         status(_("ok"));
01726         success_count++;
01727     }
01728 
01729     if (exit_status != 0)
01730         log_child_failure(exit_status);
01731 
01732     status_end();
01733 }
01734 
01735 /*
01736  * Create the summary-output files (making them empty if already existing)
01737  */
01738 static void
01739 open_result_files(void)
01740 {
01741     char        file[MAXPGPATH];
01742     FILE       *difffile;
01743 
01744     /* create the log file (copy of running status output) */
01745     snprintf(file, sizeof(file), "%s/regression.out", outputdir);
01746     logfilename = strdup(file);
01747     logfile = fopen(logfilename, "w");
01748     if (!logfile)
01749     {
01750         fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
01751                 progname, logfilename, strerror(errno));
01752         exit(2);
01753     }
01754 
01755     /* create the diffs file as empty */
01756     snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
01757     difffilename = strdup(file);
01758     difffile = fopen(difffilename, "w");
01759     if (!difffile)
01760     {
01761         fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
01762                 progname, difffilename, strerror(errno));
01763         exit(2);
01764     }
01765     /* we don't keep the diffs file open continuously */
01766     fclose(difffile);
01767 
01768     /* also create the output directory if not present */
01769     snprintf(file, sizeof(file), "%s/results", outputdir);
01770     if (!directory_exists(file))
01771         make_directory(file);
01772 }
01773 
01774 static void
01775 drop_database_if_exists(const char *dbname)
01776 {
01777     header(_("dropping database \"%s\""), dbname);
01778     psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
01779 }
01780 
01781 static void
01782 create_database(const char *dbname)
01783 {
01784     _stringlist *sl;
01785 
01786     /*
01787      * We use template0 so that any installation-local cruft in template1 will
01788      * not mess up the tests.
01789      */
01790     header(_("creating database \"%s\""), dbname);
01791     if (encoding)
01792         psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
01793                      (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
01794     else
01795         psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
01796                      (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
01797     psql_command(dbname,
01798                  "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
01799                  "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
01800                  "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
01801                  "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
01802             "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
01803                  dbname, dbname, dbname, dbname, dbname);
01804 
01805     /*
01806      * Install any requested procedural languages.  We use CREATE OR REPLACE
01807      * so that this will work whether or not the language is preinstalled.
01808      */
01809     for (sl = loadlanguage; sl != NULL; sl = sl->next)
01810     {
01811         header(_("installing %s"), sl->str);
01812         psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
01813     }
01814 
01815     /*
01816      * Install any requested extensions.  We use CREATE IF NOT EXISTS so that
01817      * this will work whether or not the extension is preinstalled.
01818      */
01819     for (sl = loadextension; sl != NULL; sl = sl->next)
01820     {
01821         header(_("installing %s"), sl->str);
01822         psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
01823     }
01824 }
01825 
01826 static void
01827 drop_role_if_exists(const char *rolename)
01828 {
01829     header(_("dropping role \"%s\""), rolename);
01830     psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
01831 }
01832 
01833 static void
01834 create_role(const char *rolename, const _stringlist * granted_dbs)
01835 {
01836     header(_("creating role \"%s\""), rolename);
01837     psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
01838     for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
01839     {
01840         psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
01841                      granted_dbs->str, rolename);
01842     }
01843 }
01844 
01845 static char *
01846 make_absolute_path(const char *in)
01847 {
01848     char       *result;
01849 
01850     if (is_absolute_path(in))
01851         result = strdup(in);
01852     else
01853     {
01854         static char cwdbuf[MAXPGPATH];
01855 
01856         if (!cwdbuf[0])
01857         {
01858             if (!getcwd(cwdbuf, sizeof(cwdbuf)))
01859             {
01860                 fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
01861                 exit(2);
01862             }
01863         }
01864 
01865         result = malloc(strlen(cwdbuf) + strlen(in) + 2);
01866         sprintf(result, "%s/%s", cwdbuf, in);
01867     }
01868 
01869     canonicalize_path(result);
01870     return result;
01871 }
01872 
01873 static void
01874 help(void)
01875 {
01876     printf(_("PostgreSQL regression test driver\n"));
01877     printf(_("\n"));
01878     printf(_("Usage:\n  %s [OPTION]... [EXTRA-TEST]...\n"), progname);
01879     printf(_("\n"));
01880     printf(_("Options:\n"));
01881     printf(_("  --create-role=ROLE        create the specified role before testing\n"));
01882     printf(_("  --dbname=DB               use database DB (default \"regression\")\n"));
01883     printf(_("  --debug                   turn on debug mode in programs that are run\n"));
01884     printf(_("  --dlpath=DIR              look for dynamic libraries in DIR\n"));
01885     printf(_("  --encoding=ENCODING       use ENCODING as the encoding\n"));
01886     printf(_("  --inputdir=DIR            take input files from DIR (default \".\")\n"));
01887     printf(_("  --launcher=CMD            use CMD as launcher of psql\n"));
01888     printf(_("  --load-extension=EXT      load the named extension before running the\n"));
01889     printf(_("                            tests; can appear multiple times\n"));
01890     printf(_("  --load-language=LANG      load the named language before running the\n"));
01891     printf(_("                            tests; can appear multiple times\n"));
01892     printf(_("  --max-connections=N       maximum number of concurrent connections\n"));
01893     printf(_("                            (default is 0, meaning unlimited)\n"));
01894     printf(_("  --outputdir=DIR           place output files in DIR (default \".\")\n"));
01895     printf(_("  --schedule=FILE           use test ordering schedule from FILE\n"));
01896     printf(_("                            (can be used multiple times to concatenate)\n"));
01897     printf(_("  --temp-install=DIR        create a temporary installation in DIR\n"));
01898     printf(_("  --use-existing            use an existing installation\n"));
01899     printf(_("\n"));
01900     printf(_("Options for \"temp-install\" mode:\n"));
01901     printf(_("  --extra-install=DIR       additional directory to install (e.g., contrib)\n"));
01902     printf(_("  --no-locale               use C locale\n"));
01903     printf(_("  --port=PORT               start postmaster on PORT\n"));
01904     printf(_("  --temp-config=FILE        append contents of FILE to temporary config\n"));
01905     printf(_("  --top-builddir=DIR        (relative) path to top level build directory\n"));
01906     printf(_("\n"));
01907     printf(_("Options for using an existing installation:\n"));
01908     printf(_("  --host=HOST               use postmaster running on HOST\n"));
01909     printf(_("  --port=PORT               use postmaster running at PORT\n"));
01910     printf(_("  --user=USER               connect as USER\n"));
01911     printf(_("  --psqldir=DIR             use psql in DIR (default: configured bindir)\n"));
01912     printf(_("\n"));
01913     printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
01914     printf(_("if the tests could not be run for some reason.\n"));
01915     printf(_("\n"));
01916     printf(_("Report bugs to <[email protected]>.\n"));
01917 }
01918 
01919 int
01920 regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
01921 {
01922     static struct option long_options[] = {
01923         {"help", no_argument, NULL, 'h'},
01924         {"version", no_argument, NULL, 'V'},
01925         {"dbname", required_argument, NULL, 1},
01926         {"debug", no_argument, NULL, 2},
01927         {"inputdir", required_argument, NULL, 3},
01928         {"load-language", required_argument, NULL, 4},
01929         {"max-connections", required_argument, NULL, 5},
01930         {"encoding", required_argument, NULL, 6},
01931         {"outputdir", required_argument, NULL, 7},
01932         {"schedule", required_argument, NULL, 8},
01933         {"temp-install", required_argument, NULL, 9},
01934         {"no-locale", no_argument, NULL, 10},
01935         {"top-builddir", required_argument, NULL, 11},
01936         {"host", required_argument, NULL, 13},
01937         {"port", required_argument, NULL, 14},
01938         {"user", required_argument, NULL, 15},
01939         {"psqldir", required_argument, NULL, 16},
01940         {"dlpath", required_argument, NULL, 17},
01941         {"create-role", required_argument, NULL, 18},
01942         {"temp-config", required_argument, NULL, 19},
01943         {"use-existing", no_argument, NULL, 20},
01944         {"launcher", required_argument, NULL, 21},
01945         {"load-extension", required_argument, NULL, 22},
01946         {"extra-install", required_argument, NULL, 23},
01947         {NULL, 0, NULL, 0}
01948     };
01949 
01950     _stringlist *sl;
01951     int         c;
01952     int         i;
01953     int         option_index;
01954     char        buf[MAXPGPATH * 4];
01955     char        buf2[MAXPGPATH * 4];
01956 
01957     progname = get_progname(argv[0]);
01958     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
01959 
01960     atexit(stop_postmaster);
01961 
01962 #ifndef HAVE_UNIX_SOCKETS
01963     /* no unix domain sockets available, so change default */
01964     hostname = "localhost";
01965 #endif
01966 
01967     /*
01968      * We call the initialization function here because that way we can set
01969      * default parameters and let them be overwritten by the commandline.
01970      */
01971     ifunc();
01972 
01973     if (getenv("PG_REGRESS_DIFF_OPTS"))
01974         pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
01975 
01976     while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
01977     {
01978         switch (c)
01979         {
01980             case 'h':
01981                 help();
01982                 exit(0);
01983             case 'V':
01984                 puts("pg_regress (PostgreSQL) " PG_VERSION);
01985                 exit(0);
01986             case 1:
01987 
01988                 /*
01989                  * If a default database was specified, we need to remove it
01990                  * before we add the specified one.
01991                  */
01992                 free_stringlist(&dblist);
01993                 split_to_stringlist(strdup(optarg), ", ", &dblist);
01994                 break;
01995             case 2:
01996                 debug = true;
01997                 break;
01998             case 3:
01999                 inputdir = strdup(optarg);
02000                 break;
02001             case 4:
02002                 add_stringlist_item(&loadlanguage, optarg);
02003                 break;
02004             case 5:
02005                 max_connections = atoi(optarg);
02006                 break;
02007             case 6:
02008                 encoding = strdup(optarg);
02009                 break;
02010             case 7:
02011                 outputdir = strdup(optarg);
02012                 break;
02013             case 8:
02014                 add_stringlist_item(&schedulelist, optarg);
02015                 break;
02016             case 9:
02017                 temp_install = make_absolute_path(optarg);
02018                 break;
02019             case 10:
02020                 nolocale = true;
02021                 break;
02022             case 11:
02023                 top_builddir = strdup(optarg);
02024                 break;
02025             case 13:
02026                 hostname = strdup(optarg);
02027                 break;
02028             case 14:
02029                 port = atoi(optarg);
02030                 port_specified_by_user = true;
02031                 break;
02032             case 15:
02033                 user = strdup(optarg);
02034                 break;
02035             case 16:
02036                 /* "--psqldir=" should mean to use PATH */
02037                 if (strlen(optarg))
02038                     psqldir = strdup(optarg);
02039                 break;
02040             case 17:
02041                 dlpath = strdup(optarg);
02042                 break;
02043             case 18:
02044                 split_to_stringlist(strdup(optarg), ", ", &extraroles);
02045                 break;
02046             case 19:
02047                 temp_config = strdup(optarg);
02048                 break;
02049             case 20:
02050                 use_existing = true;
02051                 break;
02052             case 21:
02053                 launcher = strdup(optarg);
02054                 break;
02055             case 22:
02056                 add_stringlist_item(&loadextension, optarg);
02057                 break;
02058             case 23:
02059                 add_stringlist_item(&extra_install, optarg);
02060                 break;
02061             default:
02062                 /* getopt_long already emitted a complaint */
02063                 fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
02064                         progname);
02065                 exit(2);
02066         }
02067     }
02068 
02069     /*
02070      * if we still have arguments, they are extra tests to run
02071      */
02072     while (argc - optind >= 1)
02073     {
02074         add_stringlist_item(&extra_tests, argv[optind]);
02075         optind++;
02076     }
02077 
02078     if (temp_install && !port_specified_by_user)
02079 
02080         /*
02081          * To reduce chances of interference with parallel installations, use
02082          * a port number starting in the private range (49152-65535)
02083          * calculated from the version number.
02084          */
02085         port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
02086 
02087     inputdir = make_absolute_path(inputdir);
02088     outputdir = make_absolute_path(outputdir);
02089     dlpath = make_absolute_path(dlpath);
02090 
02091     /*
02092      * Initialization
02093      */
02094     open_result_files();
02095 
02096     initialize_environment();
02097 
02098 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
02099     unlimit_core_size();
02100 #endif
02101 
02102     if (temp_install)
02103     {
02104         FILE       *pg_conf;
02105         _stringlist *sl;
02106 
02107         /*
02108          * Prepare the temp installation
02109          */
02110         if (!top_builddir)
02111         {
02112             fprintf(stderr, _("--top-builddir must be specified when using --temp-install\n"));
02113             exit(2);
02114         }
02115 
02116         if (directory_exists(temp_install))
02117         {
02118             header(_("removing existing temp installation"));
02119             rmtree(temp_install, true);
02120         }
02121 
02122         header(_("creating temporary installation"));
02123 
02124         /* make the temp install top directory */
02125         make_directory(temp_install);
02126 
02127         /* and a directory for log files */
02128         snprintf(buf, sizeof(buf), "%s/log", outputdir);
02129         if (!directory_exists(buf))
02130             make_directory(buf);
02131 
02132         /* "make install" */
02133 #ifndef WIN32_ONLY_COMPILER
02134         snprintf(buf, sizeof(buf),
02135                  SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
02136                  makeprog, top_builddir, temp_install, outputdir);
02137 #else
02138         snprintf(buf, sizeof(buf),
02139                  SYSTEMQUOTE "perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
02140                  top_builddir, temp_install, outputdir);
02141 #endif
02142         if (system(buf))
02143         {
02144             fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
02145             exit(2);
02146         }
02147 
02148         for (sl = extra_install; sl != NULL; sl = sl->next)
02149         {
02150 #ifndef WIN32_ONLY_COMPILER
02151             snprintf(buf, sizeof(buf),
02152                      SYSTEMQUOTE "\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
02153                    makeprog, top_builddir, sl->str, temp_install, outputdir);
02154 #else
02155             fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n"), progname);
02156             exit(2);
02157 #endif
02158 
02159             if (system(buf))
02160             {
02161                 fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
02162                 exit(2);
02163             }
02164         }
02165 
02166         /* initdb */
02167         header(_("initializing database system"));
02168         snprintf(buf, sizeof(buf),
02169                  SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean --nosync%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
02170                  bindir, temp_install, datadir,
02171                  debug ? " --debug" : "",
02172                  nolocale ? " --no-locale" : "",
02173                  outputdir);
02174         if (system(buf))
02175         {
02176             fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
02177             exit(2);
02178         }
02179 
02180         /*
02181          * Adjust the default postgresql.conf as needed for regression
02182          * testing. The user can specify a file to be appended; in any case we
02183          * set max_prepared_transactions to enable testing of prepared xacts.
02184          * (Note: to reduce the probability of unexpected shmmax failures,
02185          * don't set max_prepared_transactions any higher than actually needed
02186          * by the prepared_xacts regression test.)
02187          */
02188         snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install);
02189         pg_conf = fopen(buf, "a");
02190         if (pg_conf == NULL)
02191         {
02192             fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
02193             exit(2);
02194         }
02195         fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
02196         fputs("max_prepared_transactions = 2\n", pg_conf);
02197 
02198         if (temp_config != NULL)
02199         {
02200             FILE       *extra_conf;
02201             char        line_buf[1024];
02202 
02203             extra_conf = fopen(temp_config, "r");
02204             if (extra_conf == NULL)
02205             {
02206                 fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno));
02207                 exit(2);
02208             }
02209             while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
02210                 fputs(line_buf, pg_conf);
02211             fclose(extra_conf);
02212         }
02213 
02214         fclose(pg_conf);
02215 
02216         /*
02217          * Check if there is a postmaster running already.
02218          */
02219         snprintf(buf2, sizeof(buf2),
02220                  SYSTEMQUOTE "\"%s/psql\" -X postgres <%s 2>%s" SYSTEMQUOTE,
02221                  bindir, DEVNULL, DEVNULL);
02222 
02223         for (i = 0; i < 16; i++)
02224         {
02225             if (system(buf2) == 0)
02226             {
02227                 char        s[16];
02228 
02229                 if (port_specified_by_user || i == 15)
02230                 {
02231                     fprintf(stderr, _("port %d apparently in use\n"), port);
02232                     if (!port_specified_by_user)
02233                         fprintf(stderr, _("%s: could not determine an available port\n"), progname);
02234                     fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n"));
02235                     exit(2);
02236                 }
02237 
02238                 fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1);
02239                 port++;
02240                 sprintf(s, "%d", port);
02241                 doputenv("PGPORT", s);
02242             }
02243             else
02244                 break;
02245         }
02246 
02247         /*
02248          * Start the temp postmaster
02249          */
02250         header(_("starting postmaster"));
02251         snprintf(buf, sizeof(buf),
02252                  SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s -c \"listen_addresses=%s\" > \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE,
02253                  bindir, temp_install,
02254                  debug ? " -d 5" : "",
02255                  hostname ? hostname : "",
02256                  outputdir);
02257         postmaster_pid = spawn_process(buf);
02258         if (postmaster_pid == INVALID_PID)
02259         {
02260             fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"),
02261                     progname, strerror(errno));
02262             exit(2);
02263         }
02264 
02265         /*
02266          * Wait till postmaster is able to accept connections (normally only a
02267          * second or so, but Cygwin is reportedly *much* slower).  Don't wait
02268          * forever, however.
02269          */
02270         for (i = 0; i < 60; i++)
02271         {
02272             /* Done if psql succeeds */
02273             if (system(buf2) == 0)
02274                 break;
02275 
02276             /*
02277              * Fail immediately if postmaster has exited
02278              */
02279 #ifndef WIN32
02280             if (kill(postmaster_pid, 0) != 0)
02281 #else
02282             if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
02283 #endif
02284             {
02285                 fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
02286                 exit(2);
02287             }
02288 
02289             pg_usleep(1000000L);
02290         }
02291         if (i >= 60)
02292         {
02293             fprintf(stderr, _("\n%s: postmaster did not respond within 60 seconds\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
02294 
02295             /*
02296              * If we get here, the postmaster is probably wedged somewhere in
02297              * startup.  Try to kill it ungracefully rather than leaving a
02298              * stuck postmaster that might interfere with subsequent test
02299              * attempts.
02300              */
02301 #ifndef WIN32
02302             if (kill(postmaster_pid, SIGKILL) != 0 &&
02303                 errno != ESRCH)
02304                 fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n"),
02305                         progname, strerror(errno));
02306 #else
02307             if (TerminateProcess(postmaster_pid, 255) == 0)
02308                 fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n"),
02309                         progname, GetLastError());
02310 #endif
02311 
02312             exit(2);
02313         }
02314 
02315         postmaster_running = true;
02316 
02317 #ifdef WIN64
02318 /* need a series of two casts to convert HANDLE without compiler warning */
02319 #define ULONGPID(x) (unsigned long) (unsigned long long) (x)
02320 #else
02321 #define ULONGPID(x) (unsigned long) (x)
02322 #endif
02323         printf(_("running on port %d with PID %lu\n"),
02324                port, ULONGPID(postmaster_pid));
02325     }
02326     else
02327     {
02328         /*
02329          * Using an existing installation, so may need to get rid of
02330          * pre-existing database(s) and role(s)
02331          */
02332         if (!use_existing)
02333         {
02334             for (sl = dblist; sl; sl = sl->next)
02335                 drop_database_if_exists(sl->str);
02336             for (sl = extraroles; sl; sl = sl->next)
02337                 drop_role_if_exists(sl->str);
02338         }
02339     }
02340 
02341     /*
02342      * Create the test database(s) and role(s)
02343      */
02344     if (!use_existing)
02345     {
02346         for (sl = dblist; sl; sl = sl->next)
02347             create_database(sl->str);
02348         for (sl = extraroles; sl; sl = sl->next)
02349             create_role(sl->str, dblist);
02350     }
02351 
02352     /*
02353      * Ready to run the tests
02354      */
02355     header(_("running regression test queries"));
02356 
02357     for (sl = schedulelist; sl != NULL; sl = sl->next)
02358     {
02359         run_schedule(sl->str, tfunc);
02360     }
02361 
02362     for (sl = extra_tests; sl != NULL; sl = sl->next)
02363     {
02364         run_single_test(sl->str, tfunc);
02365     }
02366 
02367     /*
02368      * Shut down temp installation's postmaster
02369      */
02370     if (temp_install)
02371     {
02372         header(_("shutting down postmaster"));
02373         stop_postmaster();
02374     }
02375 
02376     fclose(logfile);
02377 
02378     /*
02379      * Emit nice-looking summary message
02380      */
02381     if (fail_count == 0 && fail_ignore_count == 0)
02382         snprintf(buf, sizeof(buf),
02383                  _(" All %d tests passed. "),
02384                  success_count);
02385     else if (fail_count == 0)   /* fail_count=0, fail_ignore_count>0 */
02386         snprintf(buf, sizeof(buf),
02387                  _(" %d of %d tests passed, %d failed test(s) ignored. "),
02388                  success_count,
02389                  success_count + fail_ignore_count,
02390                  fail_ignore_count);
02391     else if (fail_ignore_count == 0)    /* fail_count>0 && fail_ignore_count=0 */
02392         snprintf(buf, sizeof(buf),
02393                  _(" %d of %d tests failed. "),
02394                  fail_count,
02395                  success_count + fail_count);
02396     else
02397         /* fail_count>0 && fail_ignore_count>0 */
02398         snprintf(buf, sizeof(buf),
02399                  _(" %d of %d tests failed, %d of these failures ignored. "),
02400                  fail_count + fail_ignore_count,
02401                  success_count + fail_count + fail_ignore_count,
02402                  fail_ignore_count);
02403 
02404     putchar('\n');
02405     for (i = strlen(buf); i > 0; i--)
02406         putchar('=');
02407     printf("\n%s\n", buf);
02408     for (i = strlen(buf); i > 0; i--)
02409         putchar('=');
02410     putchar('\n');
02411     putchar('\n');
02412 
02413     if (file_size(difffilename) > 0)
02414     {
02415         printf(_("The differences that caused some tests to fail can be viewed in the\n"
02416                  "file \"%s\".  A copy of the test summary that you see\n"
02417                  "above is saved in the file \"%s\".\n\n"),
02418                difffilename, logfilename);
02419     }
02420     else
02421     {
02422         unlink(difffilename);
02423         unlink(logfilename);
02424     }
02425 
02426     if (fail_count != 0)
02427         exit(1);
02428 
02429     return 0;
02430 }