]> granicus.if.org Git - postgresql/blobdiff - src/test/regress/pg_regress.c
Make the order of the header file includes consistent in non-backend modules.
[postgresql] / src / test / regress / pg_regress.c
index cb092f9d82112c84391d0a7c6ac5b48aaa6973b0..6554ce214bc02c3a5e08d51dc2f91667a45c58d6 100644 (file)
@@ -8,7 +8,7 @@
  *
  * This code is released under the terms of the PostgreSQL License.
  *
- * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * src/test/regress/pg_regress.c
@@ -16,7 +16,7 @@
  *-------------------------------------------------------------------------
  */
 
-#include "pg_regress.h"
+#include "postgres_fe.h"
 
 #include <ctype.h>
 #include <sys/stat.h>
 #include <sys/resource.h>
 #endif
 
+#include "common/logging.h"
+#include "common/restricted_token.h"
 #include "common/username.h"
 #include "getopt_long.h"
 #include "libpq/pqcomm.h"              /* needed for UNIXSOCK_PATH() */
 #include "pg_config_paths.h"
+#include "pg_regress.h"
+#include "portability/instr_time.h"
 
 /* for resultmap we need a list of pairs of strings */
 typedef struct _resultmap
@@ -44,25 +48,10 @@ typedef struct _resultmap
 } _resultmap;
 
 /*
- * Values obtained from pg_config_paths.h and Makefile.  The PG installation
- * paths are only used in temp_install mode: we use these strings to find
- * out where "make install" will put stuff under the temp_install directory.
- * In non-temp_install mode, the only thing we need is the location of psql,
- * which we expect to find in psqldir, or in the PATH if psqldir isn't given.
- *
- * XXX Because pg_regress is not installed in bindir, we can't support
- * this for relocatable trees as it is.  --psqldir would need to be
- * specified in those cases.
+ * Values obtained from Makefile.
  */
-char      *bindir = PGBINDIR;
-char      *libdir = LIBDIR;
-char      *datadir = PGSHAREDIR;
 char      *host_platform = HOST_TUPLE;
 
-#ifndef WIN32_ONLY_COMPILER
-static char *makeprog = MAKEPROG;
-#endif
-
 #ifndef WIN32                                  /* not used in WIN32 case */
 static char *shellprog = SHELLPROG;
 #endif
@@ -74,10 +63,10 @@ static char *shellprog = SHELLPROG;
  */
 #ifndef WIN32
 const char *basic_diff_opts = "";
-const char *pretty_diff_opts = "-C3";
+const char *pretty_diff_opts = "-U3";
 #else
 const char *basic_diff_opts = "-w";
-const char *pretty_diff_opts = "-w -C3";
+const char *pretty_diff_opts = "-w -U3";
 #endif
 
 /* options settable from command line */
@@ -85,17 +74,17 @@ _stringlist *dblist = NULL;
 bool           debug = false;
 char      *inputdir = ".";
 char      *outputdir = ".";
-char      *psqldir = PGBINDIR;
+char      *bindir = PGBINDIR;
 char      *launcher = NULL;
 static _stringlist *loadlanguage = NULL;
 static _stringlist *loadextension = NULL;
 static int     max_connections = 0;
+static int     max_concurrent_tests = 0;
 static char *encoding = NULL;
 static _stringlist *schedulelist = NULL;
 static _stringlist *extra_tests = NULL;
-static char *temp_install = NULL;
-static char *temp_config = NULL;
-static char *top_builddir = NULL;
+static char *temp_instance = NULL;
+static _stringlist *temp_configs = NULL;
 static bool nolocale = false;
 static bool use_existing = false;
 static char *hostname = NULL;
@@ -104,7 +93,6 @@ static bool port_specified_by_user = false;
 static char *dlpath = PKGLIBDIR;
 static char *user = NULL;
 static _stringlist *extraroles = NULL;
-static _stringlist *extra_install = NULL;
 static char *config_auth_datadir = NULL;
 
 /* internal variables */
@@ -131,30 +119,9 @@ static int fail_ignore_count = 0;
 static bool directory_exists(const char *dir);
 static void make_directory(const char *dir);
 
-static void
-header(const char *fmt,...)
-/* This extension allows gcc to check the format string for consistency with
-   the supplied arguments. */
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
-static void
-status(const char *fmt,...)
-/* This extension allows gcc to check the format string for consistency with
-   the supplied arguments. */
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
-static void
-psql_command(const char *database, const char *query,...)
-/* This extension allows gcc to check the format string for consistency with
-   the supplied arguments. */
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
-
-#ifdef WIN32
-typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
-
-/* Windows API define missing from some versions of MingW headers */
-#ifndef  DISABLE_MAX_PRIVILEGE
-#define DISABLE_MAX_PRIVILEGE  0x1
-#endif
-#endif
+static void header(const char *fmt,...) pg_attribute_printf(1, 2);
+static void status(const char *fmt,...) pg_attribute_printf(1, 2);
+static void psql_command(const char *database, const char *query,...) pg_attribute_printf(2, 3);
 
 /*
  * allow core files if possible.
@@ -188,10 +155,10 @@ unlimit_core_size(void)
 void
 add_stringlist_item(_stringlist **listhead, const char *str)
 {
-       _stringlist *newentry = malloc(sizeof(_stringlist));
+       _stringlist *newentry = pg_malloc(sizeof(_stringlist));
        _stringlist *oldentry;
 
-       newentry->str = strdup(str);
+       newentry->str = pg_strdup(str);
        newentry->next = NULL;
        if (*listhead == NULL)
                *listhead = newentry;
@@ -224,7 +191,7 @@ free_stringlist(_stringlist **listhead)
 static void
 split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
 {
-       char       *sc = strdup(s);
+       char       *sc = pg_strdup(s);
        char       *token = strtok(sc, delim);
 
        while (token)
@@ -302,8 +269,10 @@ stop_postmaster(void)
                fflush(stderr);
 
                snprintf(buf, sizeof(buf),
-                                "\"%s/pg_ctl\" stop -D \"%s/data\" -s -m fast",
-                                bindir, temp_install);
+                                "\"%s%spg_ctl\" stop -D \"%s/data\" -s",
+                                bindir ? bindir : "",
+                                bindir ? "/" : "",
+                                temp_instance);
                r = system(buf);
                if (r != 0)
                {
@@ -362,7 +331,7 @@ signal_remove_temp(int signum)
 static const char *
 make_temp_sockdir(void)
 {
-       char       *template = strdup("/tmp/pg_regress-XXXXXX");
+       char       *template = pg_strdup("/tmp/pg_regress-XXXXXX");
 
        temp_sockdir = mkdtemp(template);
        if (temp_sockdir == NULL)
@@ -390,7 +359,7 @@ make_temp_sockdir(void)
 
        return temp_sockdir;
 }
-#endif   /* HAVE_UNIX_SOCKETS */
+#endif                                                 /* HAVE_UNIX_SOCKETS */
 
 /*
  * Check whether string matches pattern
@@ -470,13 +439,13 @@ string_matches_pattern(const char *str, const char *pattern)
  * NOTE: Assumes there is enough room in the target buffer!
  */
 void
-replace_string(char *string, char *replace, char *replacement)
+replace_string(char *string, const char *replace, const char *replacement)
 {
        char       *ptr;
 
        while ((ptr = strstr(string, replace)) != NULL)
        {
-               char       *dup = strdup(string);
+               char       *dup = pg_strdup(string);
 
                strlcpy(string, dup, ptr - string + 1);
                strcat(string, replacement);
@@ -492,7 +461,7 @@ replace_string(char *string, char *replace, char *replacement)
  * the given suffix.
  */
 static void
-convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, char *suffix)
+convert_sourcefiles_in(const char *source_subdir, const char *dest_dir, const char *dest_subdir, const char *suffix)
 {
        char            testtablespace[MAXPGPATH];
        char            indir[MAXPGPATH];
@@ -537,8 +506,8 @@ convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, c
        if (directory_exists(testtablespace))
                if (!rmtree(testtablespace, true))
                {
-                       fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\": %s\n"),
-                                       progname, testtablespace, strerror(errno));
+                       fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\"\n"),
+                                       progname, testtablespace);
                        exit(2);
                }
        make_directory(testtablespace);
@@ -613,7 +582,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, c
 static void
 convert_sourcefiles(void)
 {
-       convert_sourcefiles_in("input", inputdir, "sql", "sql");
+       convert_sourcefiles_in("input", outputdir, "sql", "sql");
        convert_sourcefiles_in("output", outputdir, "expected", "out");
 }
 
@@ -696,11 +665,11 @@ load_resultmap(void)
                 */
                if (string_matches_pattern(host_platform, platform))
                {
-                       _resultmap *entry = malloc(sizeof(_resultmap));
+                       _resultmap *entry = pg_malloc(sizeof(_resultmap));
 
-                       entry->test = strdup(buf);
-                       entry->type = strdup(file_type);
-                       entry->resultfile = strdup(expected);
+                       entry->test = pg_strdup(buf);
+                       entry->type = pg_strdup(file_type);
+                       entry->resultfile = pg_strdup(expected);
                        entry->next = resultmap;
                        resultmap = entry;
                }
@@ -750,33 +719,16 @@ doputenv(const char *var, const char *val)
        putenv(s);
 }
 
-/*
- * Set the environment variable "pathname", prepending "addval" to its
- * old value (if any).
- */
-static void
-add_to_path(const char *pathname, char separator, const char *addval)
-{
-       char       *oldval = getenv(pathname);
-       char       *newval;
-
-       if (!oldval || !oldval[0])
-       {
-               /* no previous value */
-               newval = psprintf("%s=%s", pathname, addval);
-       }
-       else
-               newval = psprintf("%s=%s%c%s", pathname, addval, separator, oldval);
-
-       putenv(newval);
-}
-
 /*
  * Prepare environment variables for running regression tests
  */
 static void
 initialize_environment(void)
 {
+       /*
+        * Set default application_name.  (The test_function may choose to
+        * override this, but if it doesn't, we have something useful in place.)
+        */
        putenv("PGAPPNAME=pg_regress");
 
        if (nolocale)
@@ -790,9 +742,17 @@ initialize_environment(void)
                unsetenv("LC_NUMERIC");
                unsetenv("LC_TIME");
                unsetenv("LANG");
-               /* On Windows the default locale cannot be English, so force it */
-#if defined(WIN32) || defined(__CYGWIN__)
-               putenv("LANG=en");
+
+               /*
+                * Most platforms have adopted the POSIX locale as their
+                * implementation-defined default locale.  Exceptions include native
+                * Windows, macOS with --enable-nls, and Cygwin with --enable-nls.
+                * (Use of --enable-nls matters because libintl replaces setlocale().)
+                * Also, PostgreSQL does not support macOS with locale environment
+                * variables unset; see PostmasterMain().
+                */
+#if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
+               putenv("LANG=C");
 #endif
        }
 
@@ -837,7 +797,7 @@ initialize_environment(void)
                putenv(new_pgoptions);
        }
 
-       if (temp_install)
+       if (temp_instance)
        {
                /*
                 * Clear out any environment vars that might cause psql to connect to
@@ -875,49 +835,6 @@ initialize_environment(void)
                        sprintf(s, "%d", port);
                        doputenv("PGPORT", s);
                }
-
-               /*
-                * GNU make stores some flags in the MAKEFLAGS environment variable to
-                * pass arguments to its own children.  If we are invoked by make,
-                * that causes the make invoked by us to think its part of the make
-                * task invoking us, and so it tries to communicate with the toplevel
-                * make.  Which fails.
-                *
-                * Unset the variable to protect against such problems.  We also reset
-                * MAKELEVEL to be certain the child doesn't notice the make above us.
-                */
-               unsetenv("MAKEFLAGS");
-               unsetenv("MAKELEVEL");
-
-               /*
-                * Adjust path variables to point into the temp-install tree
-                */
-               bindir = psprintf("%s/install/%s", temp_install, bindir);
-
-               libdir = psprintf("%s/install/%s", temp_install, libdir);
-
-               datadir = psprintf("%s/install/%s", temp_install, datadir);
-
-               /* psql will be installed into temp-install bindir */
-               psqldir = bindir;
-
-               /*
-                * Set up shared library paths to include the temp install.
-                *
-                * LD_LIBRARY_PATH covers many platforms.  DYLD_LIBRARY_PATH works on
-                * Darwin, and maybe other Mach-based systems.  LIBPATH is for AIX.
-                * Windows needs shared libraries in PATH (only those linked into
-                * executables, not dlopen'ed ones). Feel free to account for others
-                * as well.
-                */
-               add_to_path("LD_LIBRARY_PATH", ':', libdir);
-               add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
-               add_to_path("LIBPATH", ':', libdir);
-#if defined(WIN32)
-               add_to_path("PATH", ';', libdir);
-#elif defined(__CYGWIN__)
-               add_to_path("PATH", ':', libdir);
-#endif
        }
        else
        {
@@ -943,6 +860,14 @@ initialize_environment(void)
                if (user != NULL)
                        doputenv("PGUSER", user);
 
+               /*
+                * However, we *don't* honor PGDATABASE, since we certainly don't wish
+                * to connect to whatever database the user might like as default.
+                * (Most tests override PGDATABASE anyway, but there are some ECPG
+                * test cases that don't.)
+                */
+               unsetenv("PGDATABASE");
+
                /*
                 * Report what we're connecting to
                 */
@@ -968,6 +893,30 @@ initialize_environment(void)
 }
 
 #ifdef ENABLE_SSPI
+
+/* support for config_sspi_auth() */
+static const char *
+fmtHba(const char *raw)
+{
+       static char *ret;
+       const char *rp;
+       char       *wp;
+
+       wp = ret = realloc(ret, 3 + strlen(raw) * 2);
+
+       *wp++ = '"';
+       for (rp = raw; *rp; rp++)
+       {
+               if (*rp == '"')
+                       *wp++ = '"';
+               *wp++ = *rp;
+       }
+       *wp++ = '"';
+       *wp++ = '\0';
+
+       return ret;
+}
+
 /*
  * Get account and domain/realm names for the current user.  This is based on
  * pg_SSPI_recvauth().  The returned strings use static storage.
@@ -995,15 +944,15 @@ current_windows_user(const char **acct, const char **dom)
        if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
        {
                fprintf(stderr,
-                               _("%s: could not get token user size: error code %lu\n"),
+                               _("%s: could not get token information buffer size: error code %lu\n"),
                                progname, GetLastError());
                exit(2);
        }
-       tokenuser = malloc(retlen);
+       tokenuser = pg_malloc(retlen);
        if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
        {
                fprintf(stderr,
-                               _("%s: could not get token user: error code %lu\n"),
+                               _("%s: could not get token information: error code %lu\n"),
                                progname, GetLastError());
                exit(2);
        }
@@ -1027,31 +976,64 @@ current_windows_user(const char **acct, const char **dom)
  * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication.  Permit
  * the current OS user to authenticate as the bootstrap superuser and as any
  * user named in a --create-role option.
+ *
+ * In --config-auth mode, the --user switch can be used to specify the
+ * bootstrap superuser's name, otherwise we assume it is the default.
  */
 static void
-config_sspi_auth(const char *pgdata)
+config_sspi_auth(const char *pgdata, const char *superuser_name)
 {
        const char *accountname,
                           *domainname;
-       const char *username;
        char       *errstr;
+       bool            have_ipv6;
        char            fname[MAXPGPATH];
        int                     res;
        FILE       *hba,
                           *ident;
        _stringlist *sl;
 
+       /* Find out the name of the current OS user */
+       current_windows_user(&accountname, &domainname);
+
+       /* Determine the bootstrap superuser's name */
+       if (superuser_name == NULL)
+       {
+               /*
+                * Compute the default superuser name the same way initdb does.
+                *
+                * It's possible that this result always matches "accountname", the
+                * value SSPI authentication discovers.  But the underlying system
+                * functions do not clearly guarantee that.
+                */
+               superuser_name = get_user_name(&errstr);
+               if (superuser_name == NULL)
+               {
+                       fprintf(stderr, "%s: %s\n", progname, errstr);
+                       exit(2);
+               }
+       }
+
        /*
-        * "username", the initdb-chosen bootstrap superuser name, may always
-        * match "accountname", the value SSPI authentication discovers.  The
-        * underlying system functions do not clearly guarantee that.
+        * Like initdb.c:setup_config(), determine whether the platform recognizes
+        * ::1 (IPv6 loopback) as a numeric host address string.
         */
-       current_windows_user(&accountname, &domainname);
-       username = get_user_name(&errstr);
-       if (username == NULL)
        {
-               fprintf(stderr, "%s: %s\n", progname, errstr);
-               exit(2);
+               struct addrinfo *gai_result;
+               struct addrinfo hints;
+               WSADATA         wsaData;
+
+               hints.ai_flags = AI_NUMERICHOST;
+               hints.ai_family = AF_UNSPEC;
+               hints.ai_socktype = 0;
+               hints.ai_protocol = 0;
+               hints.ai_addrlen = 0;
+               hints.ai_canonname = NULL;
+               hints.ai_addr = NULL;
+               hints.ai_next = NULL;
+
+               have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 &&
+                                        getaddrinfo("::1", NULL, &hints, &gai_result) == 0);
        }
 
        /* Check a Write outcome and report any error. */
@@ -1066,7 +1048,7 @@ config_sspi_auth(const char *pgdata)
        } while (0)
 
        res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
-       if (res < 0 || res >= sizeof(fname) - 1)
+       if (res < 0 || res >= sizeof(fname))
        {
                /*
                 * Truncating this name is a fatal error, because we must not fail to
@@ -1085,6 +1067,9 @@ config_sspi_auth(const char *pgdata)
        CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
        CW(fputs("host all all 127.0.0.1/32  sspi include_realm=1 map=regress\n",
                         hba) >= 0);
+       if (have_ipv6)
+               CW(fputs("host all all ::1/128  sspi include_realm=1 map=regress\n",
+                                hba) >= 0);
        CW(fclose(hba) == 0);
 
        snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
@@ -1102,14 +1087,15 @@ config_sspi_auth(const char *pgdata)
         * '#'.  Windows forbids the double-quote character itself, so don't
         * bother escaping embedded double-quote characters.
         */
-       CW(fprintf(ident, "regress  \"%s@%s\"  \"%s\"\n",
-                          accountname, domainname, username) >= 0);
+       CW(fprintf(ident, "regress  \"%s@%s\"  %s\n",
+                          accountname, domainname, fmtHba(superuser_name)) >= 0);
        for (sl = extraroles; sl; sl = sl->next)
-               CW(fprintf(ident, "regress  \"%s@%s\"  \"%s\"\n",
-                                  accountname, domainname, sl->str) >= 0);
+               CW(fprintf(ident, "regress  \"%s@%s\"  %s\n",
+                                  accountname, domainname, fmtHba(sl->str)) >= 0);
        CW(fclose(ident) == 0);
 }
-#endif
+
+#endif                                                 /* ENABLE_SSPI */
 
 /*
  * Issue a command via psql, connecting to the specified database
@@ -1144,8 +1130,8 @@ psql_command(const char *database, const char *query,...)
        /* And now we can build and execute the shell command */
        snprintf(psql_cmd, sizeof(psql_cmd),
                         "\"%s%spsql\" -X -c \"%s\" \"%s\"",
-                        psqldir ? psqldir : "",
-                        psqldir ? "/" : "",
+                        bindir ? bindir : "",
+                        bindir ? "/" : "",
                         query_escaped,
                         database);
 
@@ -1204,100 +1190,17 @@ spawn_process(const char *cmdline)
        /* in parent */
        return pid;
 #else
-       char       *cmdline2;
-       BOOL            b;
-       STARTUPINFO si;
        PROCESS_INFORMATION pi;
-       HANDLE          origToken;
+       char       *cmdline2;
        HANDLE          restrictedToken;
-       SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
-       SID_AND_ATTRIBUTES dropSids[2];
-       __CreateRestrictedToken _CreateRestrictedToken = NULL;
-       HANDLE          Advapi32Handle;
-
-       ZeroMemory(&si, sizeof(si));
-       si.cb = sizeof(si);
-
-       Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
-       if (Advapi32Handle != NULL)
-       {
-               _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
-       }
-
-       if (_CreateRestrictedToken == NULL)
-       {
-               if (Advapi32Handle != NULL)
-                       FreeLibrary(Advapi32Handle);
-               fprintf(stderr, _("%s: cannot create restricted tokens on this platform\n"),
-                               progname);
-               exit(2);
-       }
-
-       /* Open the current token to use as base for the restricted one */
-       if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
-       {
-               fprintf(stderr, _("could not open process token: error code %lu\n"),
-                               GetLastError());
-               exit(2);
-       }
-
-       /* Allocate list of SIDs to remove */
-       ZeroMemory(&dropSids, sizeof(dropSids));
-       if (!AllocateAndInitializeSid(&NtAuthority, 2,
-                                                                 SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &dropSids[0].Sid) ||
-               !AllocateAndInitializeSid(&NtAuthority, 2,
-                                                                 SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &dropSids[1].Sid))
-       {
-               fprintf(stderr, _("could not allocate SIDs: error code %lu\n"), GetLastError());
-               exit(2);
-       }
-
-       b = _CreateRestrictedToken(origToken,
-                                                          DISABLE_MAX_PRIVILEGE,
-                                                          sizeof(dropSids) / sizeof(dropSids[0]),
-                                                          dropSids,
-                                                          0, NULL,
-                                                          0, NULL,
-                                                          &restrictedToken);
-
-       FreeSid(dropSids[1].Sid);
-       FreeSid(dropSids[0].Sid);
-       CloseHandle(origToken);
-       FreeLibrary(Advapi32Handle);
-
-       if (!b)
-       {
-               fprintf(stderr, _("could not create restricted token: error code %lu\n"),
-                               GetLastError());
-               exit(2);
-       }
 
+       memset(&pi, 0, sizeof(pi));
        cmdline2 = psprintf("cmd /c \"%s\"", cmdline);
 
-#ifndef __CYGWIN__
-       AddUserToTokenDacl(restrictedToken);
-#endif
-
-       if (!CreateProcessAsUser(restrictedToken,
-                                                        NULL,
-                                                        cmdline2,
-                                                        NULL,
-                                                        NULL,
-                                                        TRUE,
-                                                        CREATE_SUSPENDED,
-                                                        NULL,
-                                                        NULL,
-                                                        &si,
-                                                        &pi))
-       {
-               fprintf(stderr, _("could not start process for \"%s\": error code %lu\n"),
-                               cmdline2, GetLastError());
+       if ((restrictedToken =
+                CreateRestrictedProcess(cmdline2, &pi)) == 0)
                exit(2);
-       }
 
-       free(cmdline2);
-
-       ResumeThread(pi.hThread);
        CloseHandle(pi.hThread);
        return pi.hProcess;
 #endif
@@ -1477,7 +1380,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul
        if (platform_expectfile)
        {
                /*
-                * Replace everything afer the last slash in expectfile with what the
+                * Replace everything after the last slash in expectfile with what the
                 * platform_expectfile contains.
                 */
                char       *p = strrchr(expectfile, '/');
@@ -1575,40 +1478,44 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul
         * Use the best comparison file to generate the "pretty" diff, which we
         * append to the diffs summary file.
         */
-       snprintf(cmd, sizeof(cmd),
-                        "diff %s \"%s\" \"%s\" >> \"%s\"",
-                        pretty_diff_opts, best_expect_file, resultsfile, difffilename);
-       run_diff(cmd, difffilename);
 
-       /* And append a separator */
+       /* Write diff header */
        difffile = fopen(difffilename, "a");
        if (difffile)
        {
                fprintf(difffile,
-                               "\n======================================================================\n\n");
+                               "diff %s %s %s\n",
+                               pretty_diff_opts, best_expect_file, resultsfile);
                fclose(difffile);
        }
 
+       /* Run diff */
+       snprintf(cmd, sizeof(cmd),
+                        "diff %s \"%s\" \"%s\" >> \"%s\"",
+                        pretty_diff_opts, best_expect_file, resultsfile, difffilename);
+       run_diff(cmd, difffilename);
+
        unlink(diff);
        return true;
 }
 
 /*
  * Wait for specified subprocesses to finish, and return their exit
- * statuses into statuses[]
+ * statuses into statuses[] and stop times into stoptimes[]
  *
  * If names isn't NULL, print each subprocess's name as it finishes
  *
  * Note: it's OK to scribble on the pids array, but not on the names array
  */
 static void
-wait_for_tests(PID_TYPE * pids, int *statuses, char **names, int num_tests)
+wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
+                          char **names, int num_tests)
 {
        int                     tests_left;
        int                     i;
 
 #ifdef WIN32
-       PID_TYPE   *active_pids = malloc(num_tests * sizeof(PID_TYPE));
+       PID_TYPE   *active_pids = pg_malloc(num_tests * sizeof(PID_TYPE));
 
        memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
 #endif
@@ -1643,7 +1550,7 @@ wait_for_tests(PID_TYPE * pids, int *statuses, char **names, int num_tests)
                p = active_pids[r - WAIT_OBJECT_0];
                /* compact the active_pids array */
                active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
-#endif   /* WIN32 */
+#endif                                                 /* WIN32 */
 
                for (i = 0; i < num_tests; i++)
                {
@@ -1655,6 +1562,7 @@ wait_for_tests(PID_TYPE * pids, int *statuses, char **names, int num_tests)
 #endif
                                pids[i] = INVALID_PID;
                                statuses[i] = (int) exit_status;
+                               INSTR_TIME_SET_CURRENT(stoptimes[i]);
                                if (names)
                                        status(" %s", names[i]);
                                tests_left--;
@@ -1682,14 +1590,9 @@ log_child_failure(int exitstatus)
 #if defined(WIN32)
                status(_(" (test process was terminated by exception 0x%X)"),
                           WTERMSIG(exitstatus));
-#elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
-               status(_(" (test process was terminated by signal %d: %s)"),
-                          WTERMSIG(exitstatus),
-                          WTERMSIG(exitstatus) < NSIG ?
-                          sys_siglist[WTERMSIG(exitstatus)] : "(unknown))");
 #else
-               status(_(" (test process was terminated by signal %d)"),
-                          WTERMSIG(exitstatus));
+               status(_(" (test process was terminated by signal %d: %s)"),
+                          WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
 #endif
        }
        else
@@ -1709,15 +1612,18 @@ run_schedule(const char *schedule, test_function tfunc)
        _stringlist *expectfiles[MAX_PARALLEL_TESTS];
        _stringlist *tags[MAX_PARALLEL_TESTS];
        PID_TYPE        pids[MAX_PARALLEL_TESTS];
+       instr_time      starttimes[MAX_PARALLEL_TESTS];
+       instr_time      stoptimes[MAX_PARALLEL_TESTS];
        int                     statuses[MAX_PARALLEL_TESTS];
        _stringlist *ignorelist = NULL;
        char            scbuf[1024];
        FILE       *scf;
        int                     line_num = 0;
 
-       memset(resultfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
-       memset(expectfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
-       memset(tags, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
+       memset(tests, 0, sizeof(tests));
+       memset(resultfiles, 0, sizeof(resultfiles));
+       memset(expectfiles, 0, sizeof(expectfiles));
+       memset(tags, 0, sizeof(tags));
 
        scf = fopen(schedule, "r");
        if (!scf)
@@ -1737,15 +1643,6 @@ run_schedule(const char *schedule, test_function tfunc)
 
                line_num++;
 
-               for (i = 0; i < MAX_PARALLEL_TESTS; i++)
-               {
-                       if (resultfiles[i] == NULL)
-                               break;
-                       free_stringlist(&resultfiles[i]);
-                       free_stringlist(&expectfiles[i]);
-                       free_stringlist(&tags[i]);
-               }
-
                /* strip trailing whitespace, especially the newline */
                i = strlen(scbuf);
                while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
@@ -1778,24 +1675,35 @@ run_schedule(const char *schedule, test_function tfunc)
 
                num_tests = 0;
                inword = false;
-               for (c = test; *c; c++)
+               for (c = test;; c++)
                {
-                       if (isspace((unsigned char) *c))
+                       if (*c == '\0' || isspace((unsigned char) *c))
                        {
-                               *c = '\0';
-                               inword = false;
+                               if (inword)
+                               {
+                                       /* Reached end of a test name */
+                                       char            sav;
+
+                                       if (num_tests >= MAX_PARALLEL_TESTS)
+                                       {
+                                               fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
+                                                               MAX_PARALLEL_TESTS, schedule, line_num, scbuf);
+                                               exit(2);
+                                       }
+                                       sav = *c;
+                                       *c = '\0';
+                                       tests[num_tests] = pg_strdup(test);
+                                       num_tests++;
+                                       *c = sav;
+                                       inword = false;
+                               }
+                               if (*c == '\0')
+                                       break;          /* loop exit is here */
                        }
                        else if (!inword)
                        {
-                               if (num_tests >= MAX_PARALLEL_TESTS)
-                               {
-                                       /* can't print scbuf here, it's already been trashed */
-                                       fprintf(stderr, _("too many parallel tests in schedule file \"%s\", line %d\n"),
-                                                       schedule, line_num);
-                                       exit(2);
-                               }
-                               tests[num_tests] = c;
-                               num_tests++;
+                               /* Start of a test name */
+                               test = c;
                                inword = true;
                        }
                }
@@ -1809,11 +1717,18 @@ run_schedule(const char *schedule, test_function tfunc)
 
                if (num_tests == 1)
                {
-                       status(_("test %-24s ... "), tests[0]);
+                       status(_("test %-28s ... "), tests[0]);
                        pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
-                       wait_for_tests(pids, statuses, NULL, 1);
+                       INSTR_TIME_SET_CURRENT(starttimes[0]);
+                       wait_for_tests(pids, statuses, stoptimes, NULL, 1);
                        /* status line is finished below */
                }
+               else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests)
+               {
+                       fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
+                                       max_concurrent_tests, schedule, line_num, scbuf);
+                       exit(2);
+               }
                else if (max_connections > 0 && max_connections < num_tests)
                {
                        int                     oldest = 0;
@@ -1825,12 +1740,15 @@ run_schedule(const char *schedule, test_function tfunc)
                                if (i - oldest >= max_connections)
                                {
                                        wait_for_tests(pids + oldest, statuses + oldest,
+                                                                  stoptimes + oldest,
                                                                   tests + oldest, i - oldest);
                                        oldest = i;
                                }
                                pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
+                               INSTR_TIME_SET_CURRENT(starttimes[i]);
                        }
                        wait_for_tests(pids + oldest, statuses + oldest,
+                                                  stoptimes + oldest,
                                                   tests + oldest, i - oldest);
                        status_end();
                }
@@ -1840,8 +1758,9 @@ run_schedule(const char *schedule, test_function tfunc)
                        for (i = 0; i < num_tests; i++)
                        {
                                pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
+                               INSTR_TIME_SET_CURRENT(starttimes[i]);
                        }
-                       wait_for_tests(pids, statuses, tests, num_tests);
+                       wait_for_tests(pids, statuses, stoptimes, tests, num_tests);
                        status_end();
                }
 
@@ -1854,7 +1773,7 @@ run_schedule(const char *schedule, test_function tfunc)
                        bool            differ = false;
 
                        if (num_tests > 1)
-                               status(_("     %-24s ... "), tests[i]);
+                               status(_("     %-28s ... "), tests[i]);
 
                        /*
                         * Advance over all three lists simultaneously.
@@ -1865,14 +1784,11 @@ run_schedule(const char *schedule, test_function tfunc)
                         */
                        for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
                                 rl != NULL;    /* rl and el have the same length */
-                                rl = rl->next, el = el->next)
+                                rl = rl->next, el = el->next,
+                                tl = tl ? tl->next : NULL)
                        {
                                bool            newdiff;
 
-                               if (tl)
-                                       tl = tl->next;          /* tl has the same length as rl and el
-                                                                                * if it exists */
-
                                newdiff = results_differ(tests[i], rl->str, el->str);
                                if (newdiff && tl)
                                {
@@ -1907,15 +1823,27 @@ run_schedule(const char *schedule, test_function tfunc)
                        }
                        else
                        {
-                               status(_("ok"));
+                               status(_("ok    "));    /* align with FAILED */
                                success_count++;
                        }
 
                        if (statuses[i] != 0)
                                log_child_failure(statuses[i]);
 
+                       INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]);
+                       status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptimes[i]));
+
                        status_end();
                }
+
+               for (i = 0; i < num_tests; i++)
+               {
+                       pg_free(tests[i]);
+                       tests[i] = NULL;
+                       free_stringlist(&resultfiles[i]);
+                       free_stringlist(&expectfiles[i]);
+                       free_stringlist(&tags[i]);
+               }
        }
 
        free_stringlist(&ignorelist);
@@ -1930,6 +1858,8 @@ static void
 run_single_test(const char *test, test_function tfunc)
 {
        PID_TYPE        pid;
+       instr_time      starttime;
+       instr_time      stoptime;
        int                     exit_status;
        _stringlist *resultfiles = NULL;
        _stringlist *expectfiles = NULL;
@@ -1939,9 +1869,10 @@ run_single_test(const char *test, test_function tfunc)
                           *tl;
        bool            differ = false;
 
-       status(_("test %-24s ... "), test);
+       status(_("test %-28s ... "), test);
        pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
-       wait_for_tests(&pid, &exit_status, NULL, 1);
+       INSTR_TIME_SET_CURRENT(starttime);
+       wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);
 
        /*
         * Advance over all three lists simultaneously.
@@ -1952,14 +1883,11 @@ run_single_test(const char *test, test_function tfunc)
         */
        for (rl = resultfiles, el = expectfiles, tl = tags;
                 rl != NULL;                    /* rl and el have the same length */
-                rl = rl->next, el = el->next)
+                rl = rl->next, el = el->next,
+                tl = tl ? tl->next : NULL)
        {
                bool            newdiff;
 
-               if (tl)
-                       tl = tl->next;          /* tl has the same length as rl and el if it
-                                                                * exists */
-
                newdiff = results_differ(test, rl->str, el->str);
                if (newdiff && tl)
                {
@@ -1975,13 +1903,16 @@ run_single_test(const char *test, test_function tfunc)
        }
        else
        {
-               status(_("ok"));
+               status(_("ok    "));    /* align with FAILED */
                success_count++;
        }
 
        if (exit_status != 0)
                log_child_failure(exit_status);
 
+       INSTR_TIME_SUBTRACT(stoptime, starttime);
+       status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptime));
+
        status_end();
 }
 
@@ -1994,9 +1925,13 @@ open_result_files(void)
        char            file[MAXPGPATH];
        FILE       *difffile;
 
+       /* create outputdir directory if not present */
+       if (!directory_exists(outputdir))
+               make_directory(outputdir);
+
        /* create the log file (copy of running status output) */
        snprintf(file, sizeof(file), "%s/regression.out", outputdir);
-       logfilename = strdup(file);
+       logfilename = pg_strdup(file);
        logfile = fopen(logfilename, "w");
        if (!logfile)
        {
@@ -2007,7 +1942,7 @@ open_result_files(void)
 
        /* create the diffs file as empty */
        snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
-       difffilename = strdup(file);
+       difffilename = pg_strdup(file);
        difffile = fopen(difffilename, "w");
        if (!difffile)
        {
@@ -2018,7 +1953,7 @@ open_result_files(void)
        /* we don't keep the diffs file open continuously */
        fclose(difffile);
 
-       /* also create the output directory if not present */
+       /* also create the results directory if not present */
        snprintf(file, sizeof(file), "%s/results", outputdir);
        if (!directory_exists(file))
                make_directory(file);
@@ -2052,8 +1987,9 @@ create_database(const char *dbname)
                                 "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
                                 "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
                                 "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
-                       "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
-                                dbname, dbname, dbname, dbname, dbname);
+                                "ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
+                                "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
+                                dbname, dbname, dbname, dbname, dbname, dbname);
 
        /*
         * Install any requested procedural languages.  We use CREATE OR REPLACE
@@ -2103,43 +2039,46 @@ help(void)
        printf(_("Usage:\n  %s [OPTION]... [EXTRA-TEST]...\n"), progname);
        printf(_("\n"));
        printf(_("Options:\n"));
-       printf(_("  --config-auth=DATADIR     update authentication settings for DATADIR\n"));
-       printf(_("  --create-role=ROLE        create the specified role before testing\n"));
-       printf(_("  --dbname=DB               use database DB (default \"regression\")\n"));
-       printf(_("  --debug                   turn on debug mode in programs that are run\n"));
-       printf(_("  --dlpath=DIR              look for dynamic libraries in DIR\n"));
-       printf(_("  --encoding=ENCODING       use ENCODING as the encoding\n"));
-       printf(_("  --inputdir=DIR            take input files from DIR (default \".\")\n"));
-       printf(_("  --launcher=CMD            use CMD as launcher of psql\n"));
-       printf(_("  --load-extension=EXT      load the named extension before running the\n"));
-       printf(_("                            tests; can appear multiple times\n"));
-       printf(_("  --load-language=LANG      load the named language before running the\n"));
-       printf(_("                            tests; can appear multiple times\n"));
-       printf(_("  --max-connections=N       maximum number of concurrent connections\n"));
-       printf(_("                            (default is 0, meaning unlimited)\n"));
-       printf(_("  --outputdir=DIR           place output files in DIR (default \".\")\n"));
-       printf(_("  --schedule=FILE           use test ordering schedule from FILE\n"));
-       printf(_("                            (can be used multiple times to concatenate)\n"));
-       printf(_("  --temp-install=DIR        create a temporary installation in DIR\n"));
-       printf(_("  --use-existing            use an existing installation\n"));
+       printf(_("      --bindir=BINPATH          use BINPATH for programs that are run;\n"));
+       printf(_("                                if empty, use PATH from the environment\n"));
+       printf(_("      --config-auth=DATADIR     update authentication settings for DATADIR\n"));
+       printf(_("      --create-role=ROLE        create the specified role before testing\n"));
+       printf(_("      --dbname=DB               use database DB (default \"regression\")\n"));
+       printf(_("      --debug                   turn on debug mode in programs that are run\n"));
+       printf(_("      --dlpath=DIR              look for dynamic libraries in DIR\n"));
+       printf(_("      --encoding=ENCODING       use ENCODING as the encoding\n"));
+       printf(_("  -h, --help                    show this help, then exit\n"));
+       printf(_("      --inputdir=DIR            take input files from DIR (default \".\")\n"));
+       printf(_("      --launcher=CMD            use CMD as launcher of psql\n"));
+       printf(_("      --load-extension=EXT      load the named extension before running the\n"));
+       printf(_("                                tests; can appear multiple times\n"));
+       printf(_("      --load-language=LANG      load the named language before running the\n"));
+       printf(_("                                tests; can appear multiple times\n"));
+       printf(_("      --max-connections=N       maximum number of concurrent connections\n"));
+       printf(_("                                (default is 0, meaning unlimited)\n"));
+       printf(_("      --max-concurrent-tests=N  maximum number of concurrent tests in schedule\n"));
+       printf(_("                                (default is 0, meaning unlimited)\n"));
+       printf(_("      --outputdir=DIR           place output files in DIR (default \".\")\n"));
+       printf(_("      --schedule=FILE           use test ordering schedule from FILE\n"));
+       printf(_("                                (can be used multiple times to concatenate)\n"));
+       printf(_("      --temp-instance=DIR       create a temporary instance in DIR\n"));
+       printf(_("      --use-existing            use an existing installation\n"));
+       printf(_("  -V, --version                 output version information, then exit\n"));
        printf(_("\n"));
-       printf(_("Options for \"temp-install\" mode:\n"));
-       printf(_("  --extra-install=DIR       additional directory to install (e.g., contrib)\n"));
-       printf(_("  --no-locale               use C locale\n"));
-       printf(_("  --port=PORT               start postmaster on PORT\n"));
-       printf(_("  --temp-config=FILE        append contents of FILE to temporary config\n"));
-       printf(_("  --top-builddir=DIR        (relative) path to top level build directory\n"));
+       printf(_("Options for \"temp-instance\" mode:\n"));
+       printf(_("      --no-locale               use C locale\n"));
+       printf(_("      --port=PORT               start postmaster on PORT\n"));
+       printf(_("      --temp-config=FILE        append contents of FILE to temporary config\n"));
        printf(_("\n"));
        printf(_("Options for using an existing installation:\n"));
-       printf(_("  --host=HOST               use postmaster running on HOST\n"));
-       printf(_("  --port=PORT               use postmaster running at PORT\n"));
-       printf(_("  --user=USER               connect as USER\n"));
-       printf(_("  --psqldir=DIR             use psql in DIR (default: configured bindir)\n"));
+       printf(_("      --host=HOST               use postmaster running on HOST\n"));
+       printf(_("      --port=PORT               use postmaster running at PORT\n"));
+       printf(_("      --user=USER               connect as USER\n"));
        printf(_("\n"));
        printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
        printf(_("if the tests could not be run for some reason.\n"));
        printf(_("\n"));
-       printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
+       printf(_("Report bugs to <pgsql-bugs@lists.postgresql.org>.\n"));
 }
 
 int
@@ -2156,21 +2095,20 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                {"encoding", required_argument, NULL, 6},
                {"outputdir", required_argument, NULL, 7},
                {"schedule", required_argument, NULL, 8},
-               {"temp-install", required_argument, NULL, 9},
+               {"temp-instance", required_argument, NULL, 9},
                {"no-locale", no_argument, NULL, 10},
-               {"top-builddir", required_argument, NULL, 11},
                {"host", required_argument, NULL, 13},
                {"port", required_argument, NULL, 14},
                {"user", required_argument, NULL, 15},
-               {"psqldir", required_argument, NULL, 16},
+               {"bindir", required_argument, NULL, 16},
                {"dlpath", required_argument, NULL, 17},
                {"create-role", required_argument, NULL, 18},
                {"temp-config", required_argument, NULL, 19},
                {"use-existing", no_argument, NULL, 20},
                {"launcher", required_argument, NULL, 21},
                {"load-extension", required_argument, NULL, 22},
-               {"extra-install", required_argument, NULL, 23},
                {"config-auth", required_argument, NULL, 24},
+               {"max-concurrent-tests", required_argument, NULL, 25},
                {NULL, 0, NULL, 0}
        };
 
@@ -2181,9 +2119,12 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
        char            buf[MAXPGPATH * 4];
        char            buf2[MAXPGPATH * 4];
 
+       pg_logging_init(argv[0]);
        progname = get_progname(argv[0]);
        set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
 
+       get_restricted_token();
+
        atexit(stop_postmaster);
 
 #ifndef HAVE_UNIX_SOCKETS
@@ -2217,13 +2158,13 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                                 * before we add the specified one.
                                 */
                                free_stringlist(&dblist);
-                               split_to_stringlist(strdup(optarg), ", ", &dblist);
+                               split_to_stringlist(optarg, ",", &dblist);
                                break;
                        case 2:
                                debug = true;
                                break;
                        case 3:
-                               inputdir = strdup(optarg);
+                               inputdir = pg_strdup(optarg);
                                break;
                        case 4:
                                add_stringlist_item(&loadlanguage, optarg);
@@ -2232,61 +2173,60 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                                max_connections = atoi(optarg);
                                break;
                        case 6:
-                               encoding = strdup(optarg);
+                               encoding = pg_strdup(optarg);
                                break;
                        case 7:
-                               outputdir = strdup(optarg);
+                               outputdir = pg_strdup(optarg);
                                break;
                        case 8:
                                add_stringlist_item(&schedulelist, optarg);
                                break;
                        case 9:
-                               temp_install = make_absolute_path(optarg);
+                               temp_instance = make_absolute_path(optarg);
                                break;
                        case 10:
                                nolocale = true;
                                break;
-                       case 11:
-                               top_builddir = strdup(optarg);
-                               break;
                        case 13:
-                               hostname = strdup(optarg);
+                               hostname = pg_strdup(optarg);
                                break;
                        case 14:
                                port = atoi(optarg);
                                port_specified_by_user = true;
                                break;
                        case 15:
-                               user = strdup(optarg);
+                               user = pg_strdup(optarg);
                                break;
                        case 16:
-                               /* "--psqldir=" should mean to use PATH */
+                               /* "--bindir=" means to use PATH */
                                if (strlen(optarg))
-                                       psqldir = strdup(optarg);
+                                       bindir = pg_strdup(optarg);
+                               else
+                                       bindir = NULL;
                                break;
                        case 17:
-                               dlpath = strdup(optarg);
+                               dlpath = pg_strdup(optarg);
                                break;
                        case 18:
-                               split_to_stringlist(strdup(optarg), ", ", &extraroles);
+                               split_to_stringlist(optarg, ",", &extraroles);
                                break;
                        case 19:
-                               temp_config = strdup(optarg);
+                               add_stringlist_item(&temp_configs, optarg);
                                break;
                        case 20:
                                use_existing = true;
                                break;
                        case 21:
-                               launcher = strdup(optarg);
+                               launcher = pg_strdup(optarg);
                                break;
                        case 22:
                                add_stringlist_item(&loadextension, optarg);
                                break;
-                       case 23:
-                               add_stringlist_item(&extra_install, optarg);
-                               break;
                        case 24:
-                               config_auth_datadir = pstrdup(optarg);
+                               config_auth_datadir = pg_strdup(optarg);
+                               break;
+                       case 25:
+                               max_concurrent_tests = atoi(optarg);
                                break;
                        default:
                                /* getopt_long already emitted a complaint */
@@ -2308,12 +2248,12 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
        if (config_auth_datadir)
        {
 #ifdef ENABLE_SSPI
-               config_sspi_auth(config_auth_datadir);
+               config_sspi_auth(config_auth_datadir, user);
 #endif
                exit(0);
        }
 
-       if (temp_install && !port_specified_by_user)
+       if (temp_instance && !port_specified_by_user)
 
                /*
                 * To reduce chances of interference with parallel installations, use
@@ -2339,79 +2279,44 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
        unlimit_core_size();
 #endif
 
-       if (temp_install)
+       if (temp_instance)
        {
                FILE       *pg_conf;
-               _stringlist *sl;
+               const char *env_wait;
+               int                     wait_seconds;
 
                /*
-                * Prepare the temp installation
+                * Prepare the temp instance
                 */
-               if (!top_builddir)
-               {
-                       fprintf(stderr, _("--top-builddir must be specified when using --temp-install\n"));
-                       exit(2);
-               }
 
-               if (directory_exists(temp_install))
+               if (directory_exists(temp_instance))
                {
-                       header(_("removing existing temp installation"));
-                       if (!rmtree(temp_install, true))
+                       header(_("removing existing temp instance"));
+                       if (!rmtree(temp_instance, true))
                        {
-                               fprintf(stderr, _("\n%s: could not remove temp installation \"%s\": %s\n"), progname, temp_install, strerror(errno));
+                               fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
+                                               progname, temp_instance);
                                exit(2);
                        }
                }
 
-               header(_("creating temporary installation"));
+               header(_("creating temporary instance"));
 
-               /* make the temp install top directory */
-               make_directory(temp_install);
+               /* make the temp instance top directory */
+               make_directory(temp_instance);
 
                /* and a directory for log files */
                snprintf(buf, sizeof(buf), "%s/log", outputdir);
                if (!directory_exists(buf))
                        make_directory(buf);
 
-               /* "make install" */
-#ifndef WIN32_ONLY_COMPILER
-               snprintf(buf, sizeof(buf),
-                                "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install > \"%s/log/install.log\" 2>&1",
-                                makeprog, top_builddir, temp_install, outputdir);
-#else
-               snprintf(buf, sizeof(buf),
-                                "perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1",
-                                top_builddir, temp_install, outputdir);
-#endif
-               if (system(buf))
-               {
-                       fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
-                       exit(2);
-               }
-
-               for (sl = extra_install; sl != NULL; sl = sl->next)
-               {
-#ifndef WIN32_ONLY_COMPILER
-                       snprintf(buf, sizeof(buf),
-                                        "\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1",
-                                  makeprog, top_builddir, sl->str, temp_install, outputdir);
-#else
-                       fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n"), progname);
-                       exit(2);
-#endif
-
-                       if (system(buf))
-                       {
-                               fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
-                               exit(2);
-                       }
-               }
-
                /* initdb */
                header(_("initializing database system"));
                snprintf(buf, sizeof(buf),
-                                "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean --nosync%s%s > \"%s/log/initdb.log\" 2>&1",
-                                bindir, temp_install, datadir,
+                                "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1",
+                                bindir ? bindir : "",
+                                bindir ? "/" : "",
+                                temp_instance,
                                 debug ? " --debug" : "",
                                 nolocale ? " --no-locale" : "",
                                 outputdir);
@@ -2422,14 +2327,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                }
 
                /*
-                * Adjust the default postgresql.conf as needed for regression
-                * testing. The user can specify a file to be appended; in any case we
-                * set max_prepared_transactions to enable testing of prepared xacts.
-                * (Note: to reduce the probability of unexpected shmmax failures,
-                * don't set max_prepared_transactions any higher than actually needed
-                * by the prepared_xacts regression test.)
+                * Adjust the default postgresql.conf for regression testing. The user
+                * can specify a file to be appended; in any case we expand logging
+                * and set max_prepared_transactions to enable testing of prepared
+                * xacts.  (Note: to reduce the probability of unexpected shmmax
+                * failures, don't set max_prepared_transactions any higher than
+                * actually needed by the prepared_xacts regression test.)
                 */
-               snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install);
+               snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
                pg_conf = fopen(buf, "a");
                if (pg_conf == NULL)
                {
@@ -2437,10 +2342,16 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                        exit(2);
                }
                fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
+               fputs("log_autovacuum_min_duration = 0\n", pg_conf);
+               fputs("log_checkpoints = on\n", pg_conf);
+               fputs("log_line_prefix = '%m [%p] %q%a '\n", pg_conf);
+               fputs("log_lock_waits = on\n", pg_conf);
+               fputs("log_temp_files = 128kB\n", pg_conf);
                fputs("max_prepared_transactions = 2\n", pg_conf);
 
-               if (temp_config != NULL)
+               for (sl = temp_configs; sl != NULL; sl = sl->next)
                {
+                       char       *temp_config = sl->str;
                        FILE       *extra_conf;
                        char            line_buf[1024];
 
@@ -2463,8 +2374,8 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                 * Since we successfully used the same buffer for the much-longer
                 * "initdb" command, this can't truncate.
                 */
-               snprintf(buf, sizeof(buf), "%s/data", temp_install);
-               config_sspi_auth(buf);
+               snprintf(buf, sizeof(buf), "%s/data", temp_instance);
+               config_sspi_auth(buf, NULL);
 #elif !defined(HAVE_UNIX_SOCKETS)
 #error Platform has no means to secure the test installation.
 #endif
@@ -2473,8 +2384,10 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                 * Check if there is a postmaster running already.
                 */
                snprintf(buf2, sizeof(buf2),
-                                "\"%s/psql\" -X postgres <%s 2>%s",
-                                bindir, DEVNULL, DEVNULL);
+                                "\"%s%spsql\" -X postgres <%s 2>%s",
+                                bindir ? bindir : "",
+                                bindir ? "/" : "",
+                                DEVNULL, DEVNULL);
 
                for (i = 0; i < 16; i++)
                {
@@ -2505,10 +2418,12 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                 */
                header(_("starting postmaster"));
                snprintf(buf, sizeof(buf),
-                                "\"%s/postgres\" -D \"%s/data\" -F%s "
+                                "\"%s%spostgres\" -D \"%s/data\" -F%s "
                                 "-c \"listen_addresses=%s\" -k \"%s\" "
                                 "> \"%s/log/postmaster.log\" 2>&1",
-                                bindir, temp_install, debug ? " -d 5" : "",
+                                bindir ? bindir : "",
+                                bindir ? "/" : "",
+                                temp_instance, debug ? " -d 5" : "",
                                 hostname ? hostname : "", sockdir ? sockdir : "",
                                 outputdir);
                postmaster_pid = spawn_process(buf);
@@ -2520,11 +2435,23 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                }
 
                /*
-                * Wait till postmaster is able to accept connections (normally only a
-                * second or so, but Cygwin is reportedly *much* slower).  Don't wait
-                * forever, however.
+                * Wait till postmaster is able to accept connections; normally this
+                * is only a second or so, but Cygwin is reportedly *much* slower, and
+                * test builds using Valgrind or similar tools might be too.  Hence,
+                * allow the default timeout of 60 seconds to be overridden from the
+                * PGCTLTIMEOUT environment variable.
                 */
-               for (i = 0; i < 60; i++)
+               env_wait = getenv("PGCTLTIMEOUT");
+               if (env_wait != NULL)
+               {
+                       wait_seconds = atoi(env_wait);
+                       if (wait_seconds <= 0)
+                               wait_seconds = 60;
+               }
+               else
+                       wait_seconds = 60;
+
+               for (i = 0; i < wait_seconds; i++)
                {
                        /* Done if psql succeeds */
                        if (system(buf2) == 0)
@@ -2534,7 +2461,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                         * Fail immediately if postmaster has exited
                         */
 #ifndef WIN32
-                       if (kill(postmaster_pid, 0) != 0)
+                       if (waitpid(postmaster_pid, NULL, WNOHANG) == postmaster_pid)
 #else
                        if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
 #endif
@@ -2545,9 +2472,10 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 
                        pg_usleep(1000000L);
                }
-               if (i >= 60)
+               if (i >= wait_seconds)
                {
-                       fprintf(stderr, _("\n%s: postmaster did not respond within 60 seconds\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
+                       fprintf(stderr, _("\n%s: postmaster did not respond within %d seconds\nExamine %s/log/postmaster.log for the reason\n"),
+                                       progname, wait_seconds, outputdir);
 
                        /*
                         * If we get here, the postmaster is probably wedged somewhere in
@@ -2571,7 +2499,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 
                postmaster_running = true;
 
-#ifdef WIN64
+#ifdef _WIN64
 /* need a series of two casts to convert HANDLE without compiler warning */
 #define ULONGPID(x) (unsigned long) (unsigned long long) (x)
 #else
@@ -2624,12 +2552,25 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
        /*
         * Shut down temp installation's postmaster
         */
-       if (temp_install)
+       if (temp_instance)
        {
                header(_("shutting down postmaster"));
                stop_postmaster();
        }
 
+       /*
+        * If there were no errors, remove the temp instance immediately to
+        * conserve disk space.  (If there were errors, we leave the instance in
+        * place for possible manual investigation.)
+        */
+       if (temp_instance && fail_count == 0 && fail_ignore_count == 0)
+       {
+               header(_("removing temporary instance"));
+               if (!rmtree(temp_instance, true))
+                       fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
+                                       progname, temp_instance);
+       }
+
        fclose(logfile);
 
        /*