]> granicus.if.org Git - postgresql/blobdiff - src/test/regress/pg_regress.c
Use a macro variable PG_PRINTF_ATTRIBUTE for the style used for checking printf type...
[postgresql] / src / test / regress / pg_regress.c
index 8d4c3dda339ff38ecf6b147c78ca764bb0224610..1411ca4e40054e6c983676c4d0eee21fbd2e591c 100644 (file)
@@ -8,10 +8,10 @@
  *
  * This code is released under the terms of the PostgreSQL License.
  *
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/test/regress/pg_regress.c,v 1.49 2008/11/09 00:28:35 tgl Exp $
+ * src/test/regress/pg_regress.c
  *
  *-------------------------------------------------------------------------
  */
@@ -65,9 +65,18 @@ static char *makeprog = MAKEPROG;
 static char *shellprog = SHELLPROG;
 #endif
 
-/* currently we can use the same diff switches on all platforms */
+/*
+ * On Windows we use -w in diff switches to avoid problems with inconsistent
+ * newline representation.     The actual result files will generally have
+ * Windows-style newlines, but the comparison files might or might not.
+ */
+#ifndef WIN32
+const char *basic_diff_opts = "";
+const char *pretty_diff_opts = "-C3";
+#else
 const char *basic_diff_opts = "-w";
 const char *pretty_diff_opts = "-w -C3";
+#endif
 
 /* options settable from command line */
 _stringlist *dblist = NULL;
@@ -75,7 +84,9 @@ bool          debug = false;
 char      *inputdir = ".";
 char      *outputdir = ".";
 char      *psqldir = PGBINDIR;
+char      *launcher = NULL;
 static _stringlist *loadlanguage = NULL;
+static _stringlist *loadextension = NULL;
 static int     max_connections = 0;
 static char *encoding = NULL;
 static _stringlist *schedulelist = NULL;
@@ -83,13 +94,15 @@ static _stringlist *extra_tests = NULL;
 static char *temp_install = NULL;
 static char *temp_config = NULL;
 static char *top_builddir = NULL;
-static int     temp_port = 65432;
 static bool nolocale = false;
+static bool use_existing = false;
 static char *hostname = NULL;
 static int     port = -1;
+static bool port_specified_by_user = false;
 static char *dlpath = PKGLIBDIR;
 static char *user = NULL;
 static _stringlist *extraroles = NULL;
+static _stringlist *extra_install = NULL;
 
 /* internal variables */
 static const char *progname;
@@ -113,24 +126,26 @@ static void
 header(const char *fmt,...)
 /* This extension allows gcc to check the format string for consistency with
    the supplied arguments. */
-__attribute__((format(printf, 1, 2)));
+__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(printf, 1, 2)));
+__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(printf, 2, 3)));
+__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);
+typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
 
-/* Windows API define missing from MingW headers */
+/* Windows API define missing from some versions of MingW headers */
+#ifndef  DISABLE_MAX_PRIVILEGE
 #define DISABLE_MAX_PRIVILEGE  0x1
 #endif
+#endif
 
 /*
  * allow core files if possible.
@@ -271,6 +286,7 @@ stop_postmaster(void)
        {
                /* We use pg_ctl to issue the kill and wait for stop */
                char            buf[MAXPGPATH * 2];
+               int                     r;
 
                /* On Windows, system() seems not to force fflush, so... */
                fflush(stdout);
@@ -279,7 +295,14 @@ stop_postmaster(void)
                snprintf(buf, sizeof(buf),
                                 SYSTEMQUOTE "\"%s/pg_ctl\" stop -D \"%s/data\" -s -m fast" SYSTEMQUOTE,
                                 bindir, temp_install);
-               system(buf);                    /* ignore exit status */
+               r = system(buf);
+               if (r != 0)
+               {
+                       fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
+                                       progname, r);
+                       exit(2);                        /* not exit_nicely(), that would be recursive */
+               }
+
                postmaster_running = false;
        }
 }
@@ -399,7 +422,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
 {
        char            testtablespace[MAXPGPATH];
        char            indir[MAXPGPATH];
-       struct stat     st;
+       struct stat st;
        int                     ret;
        char      **name;
        char      **names;
@@ -412,8 +435,8 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
        if (ret != 0 || !S_ISDIR(st.st_mode))
        {
                /*
-                * No warning, to avoid noise in tests that do not have
-                * these directories; for example, ecpg, contrib and src/pl.
+                * No warning, to avoid noise in tests that do not have these
+                * directories; for example, ecpg, contrib and src/pl.
                 */
                return;
        }
@@ -426,11 +449,12 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
        snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
 
 #ifdef WIN32
+
        /*
         * On Windows only, clean out the test tablespace dir, or create it if it
-        * doesn't exist.  On other platforms we expect the Makefile to take
-        * care of that.  (We don't migrate that functionality in here because
-        * it'd be harder to cope with platform-specific issues such as SELinux.)
+        * doesn't exist.  On other platforms we expect the Makefile to take care
+        * of that.  (We don't migrate that functionality in here because it'd be
+        * harder to cope with platform-specific issues such as SELinux.)
         *
         * XXX it would be better if pg_regress.c had nothing at all to do with
         * testtablespace, and this were handled by a .BAT file or similar on
@@ -678,25 +702,35 @@ initialize_environment(void)
 {
        char       *tmp;
 
+       if (nolocale)
+       {
+               /*
+                * Clear out any non-C locale settings
+                */
+               unsetenv("LC_COLLATE");
+               unsetenv("LC_CTYPE");
+               unsetenv("LC_MONETARY");
+               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");
+#endif
+       }
+
        /*
-        * Clear out any non-C locale settings
+        * Set translation-related settings to English; otherwise psql will
+        * produce translated messages and produce diffs.  (XXX If we ever support
+        * translation of pg_regress, this needs to be moved elsewhere, where psql
+        * is actually called.)
         */
-       unsetenv("LC_COLLATE");
-       unsetenv("LC_CTYPE");
-       unsetenv("LC_MONETARY");
-       unsetenv("LC_MESSAGES");
-       unsetenv("LC_NUMERIC");
-       unsetenv("LC_TIME");
-       unsetenv("LC_ALL");
-       unsetenv("LANG");
        unsetenv("LANGUAGE");
-       /* On Windows the default locale cannot be English, so force it */
-#if defined(WIN32) || defined(__CYGWIN__)
-       putenv("LANG=en");
-#endif
+       unsetenv("LC_ALL");
+       putenv("LC_MESSAGES=C");
 
        /*
-        * Set multibyte as requested
+        * Set encoding as requested
         */
        if (encoding)
                doputenv("PGCLIENTENCODING", encoding);
@@ -708,7 +742,23 @@ initialize_environment(void)
         */
        putenv("PGTZ=PST8PDT");
        putenv("PGDATESTYLE=Postgres, MDY");
-       putenv("PGINTERVALSTYLE=postgres_verbose");
+
+       /*
+        * Likewise set intervalstyle to ensure consistent results.  This is a bit
+        * more painful because we must use PGOPTIONS, and we want to preserve the
+        * user's ability to set other variables through that.
+        */
+       {
+               const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
+               const char *old_pgoptions = getenv("PGOPTIONS");
+               char       *new_pgoptions;
+
+               if (!old_pgoptions)
+                       old_pgoptions = "";
+               new_pgoptions = malloc(strlen(old_pgoptions) + strlen(my_pgoptions) + 12);
+               sprintf(new_pgoptions, "PGOPTIONS=%s %s", old_pgoptions, my_pgoptions);
+               putenv(new_pgoptions);
+       }
 
        if (temp_install)
        {
@@ -770,8 +820,10 @@ initialize_environment(void)
                add_to_path("LD_LIBRARY_PATH", ':', libdir);
                add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
                add_to_path("LIBPATH", ':', libdir);
-#if defined(WIN32) || defined(__CYGWIN__)
+#if defined(WIN32)
                add_to_path("PATH", ';', libdir);
+#elif defined(__CYGWIN__)
+               add_to_path("PATH", ':', libdir);
 #endif
        }
        else
@@ -986,30 +1038,30 @@ spawn_process(const char *cmdline)
        cmdline2 = malloc(strlen(cmdline) + 8);
        sprintf(cmdline2, "cmd /c %s", cmdline);
 
+#ifndef __CYGWIN__
+       AddUserToTokenDacl(restrictedToken);
+#endif
+
        if (!CreateProcessAsUser(restrictedToken,
-                                               NULL,
-                                               cmdline2,
-                                               NULL,
-                                               NULL,
-                                               TRUE,
-                                               CREATE_SUSPENDED,
-                                               NULL,
-                                               NULL,
-                                               &si,
-                                               &pi))
+                                                        NULL,
+                                                        cmdline2,
+                                                        NULL,
+                                                        NULL,
+                                                        TRUE,
+                                                        CREATE_SUSPENDED,
+                                                        NULL,
+                                                        NULL,
+                                                        &si,
+                                                        &pi))
        {
                fprintf(stderr, _("could not start process for \"%s\": %lu\n"),
                                cmdline2, GetLastError());
                exit_nicely(2);
        }
 
-#ifndef __CYGWIN__
-       AddUserToDacl(pi.hProcess);
-#endif
-
        free(cmdline2);
 
-    ResumeThread(pi.hThread);
+       ResumeThread(pi.hThread);
        CloseHandle(pi.hThread);
        return pi.hProcess;
 #endif
@@ -1110,7 +1162,11 @@ get_alternative_expectfile(const char *expectfile, int i)
        strcpy(tmp, expectfile);
        last_dot = strrchr(tmp, '.');
        if (!last_dot)
+       {
+               free(tmp);
+               free(s);
                return NULL;
+       }
        *last_dot = '\0';
        snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
        free(tmp);
@@ -1290,7 +1346,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul
  * 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, char **names, int num_tests)
 {
        int                     tests_left;
        int                     i;
@@ -1305,9 +1361,10 @@ wait_for_tests(PID_TYPE *pids, int *statuses, char **names, int num_tests)
        while (tests_left > 0)
        {
                PID_TYPE        p;
-               int                     exit_status;
 
 #ifndef WIN32
+               int                     exit_status;
+
                p = wait(&exit_status);
 
                if (p == INVALID_PID)
@@ -1317,6 +1374,7 @@ wait_for_tests(PID_TYPE *pids, int *statuses, char **names, int num_tests)
                        exit_nicely(2);
                }
 #else
+               DWORD           exit_status;
                int                     r;
 
                r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
@@ -1340,7 +1398,7 @@ wait_for_tests(PID_TYPE *pids, int *statuses, char **names, int num_tests)
                                CloseHandle(pids[i]);
 #endif
                                pids[i] = INVALID_PID;
-                               statuses[i] = exit_status;
+                               statuses[i] = (int) exit_status;
                                if (names)
                                        status(" %s", names[i]);
                                tests_left--;
@@ -1495,7 +1553,7 @@ run_schedule(const char *schedule, test_function tfunc)
 
                if (num_tests == 1)
                {
-                       status(_("test %-20s ... "), tests[0]);
+                       status(_("test %-24s ... "), tests[0]);
                        pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
                        wait_for_tests(pids, statuses, NULL, 1);
                        /* status line is finished below */
@@ -1540,7 +1598,7 @@ run_schedule(const char *schedule, test_function tfunc)
                        bool            differ = false;
 
                        if (num_tests > 1)
-                               status(_("     %-20s ... "), tests[i]);
+                               status(_("     %-24s ... "), tests[i]);
 
                        /*
                         * Advance over all three lists simultaneously.
@@ -1623,7 +1681,7 @@ run_single_test(const char *test, test_function tfunc)
                           *tl;
        bool            differ = false;
 
-       status(_("test %-20s ... "), test);
+       status(_("test %-24s ... "), test);
        pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
        wait_for_tests(&pid, &exit_status, NULL, 1);
 
@@ -1726,9 +1784,11 @@ create_database(const char *dbname)
         */
        header(_("creating database \"%s\""), dbname);
        if (encoding)
-               psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'", dbname, encoding);
+               psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
+                                        (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
        else
-               psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0", dbname);
+               psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
+                                        (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
        psql_command(dbname,
                                 "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
                                 "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
@@ -1738,12 +1798,23 @@ create_database(const char *dbname)
                                 dbname, dbname, dbname, dbname, dbname);
 
        /*
-        * Install any requested procedural languages
+        * Install any requested procedural languages.  We use CREATE OR REPLACE
+        * so that this will work whether or not the language is preinstalled.
         */
        for (sl = loadlanguage; sl != NULL; sl = sl->next)
        {
                header(_("installing %s"), sl->str);
-               psql_command(dbname, "CREATE LANGUAGE \"%s\"", sl->str);
+               psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
+       }
+
+       /*
+        * Install any requested extensions.  We use CREATE IF NOT EXISTS so that
+        * this will work whether or not the extension is preinstalled.
+        */
+       for (sl = loadextension; sl != NULL; sl = sl->next)
+       {
+               header(_("installing %s"), sl->str);
+               psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
        }
 }
 
@@ -1769,13 +1840,13 @@ create_role(const char *rolename, const _stringlist * granted_dbs)
 static char *
 make_absolute_path(const char *in)
 {
-       char *result;
+       char       *result;
 
        if (is_absolute_path(in))
                result = strdup(in);
        else
        {
-               static char             cwdbuf[MAXPGPATH];
+               static char cwdbuf[MAXPGPATH];
 
                if (!cwdbuf[0])
                {
@@ -1807,21 +1878,26 @@ help(void)
        printf(_("  --inputdir=DIR            take input files from DIR (default \".\")\n"));
        printf(_("  --load-language=lang      load the named language before running the\n"));
        printf(_("                            tests; can appear multiple times\n"));
+       printf(_("  --load-extension=ext      load the named extension before running the\n"));
+       printf(_("                            tests; can appear multiple times\n"));
        printf(_("  --create-role=ROLE        create the specified role before testing\n"));
        printf(_("  --max-connections=N       maximum number of concurrent connections\n"));
        printf(_("                            (default is 0 meaning unlimited)\n"));
-       printf(_("  --multibyte=ENCODING      use ENCODING as the multibyte encoding\n"));
+       printf(_("  --encoding=ENCODING       use ENCODING as the encoding\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(_("  --dlpath=DIR              look for dynamic libraries in DIR\n"));
        printf(_("  --temp-install=DIR        create a temporary installation in DIR\n"));
+       printf(_("  --use-existing            use an existing installation\n"));
+       printf(_("  --launcher=CMD            use CMD as launcher of psql\n"));
        printf(_("\n"));
        printf(_("Options for \"temp-install\" mode:\n"));
        printf(_("  --no-locale               use C locale\n"));
        printf(_("  --top-builddir=DIR        (relative) path to top level build directory\n"));
-       printf(_("  --temp-port=PORT          port number to start temp postmaster on\n"));
+       printf(_("  --port=PORT               start postmaster on PORT\n"));
        printf(_("  --temp-config=PATH        append contents of PATH to temporary config\n"));
+       printf(_("  --extra-install=DIR       additional directory to install (e.g., contrib\n"));
        printf(_("\n"));
        printf(_("Options for using an existing installation:\n"));
        printf(_("  --host=HOST               use postmaster running on HOST\n"));
@@ -1843,6 +1919,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
        int                     i;
        int                     option_index;
        char            buf[MAXPGPATH * 4];
+       char            buf2[MAXPGPATH * 4];
 
        static struct option long_options[] = {
                {"help", no_argument, NULL, 'h'},
@@ -1852,13 +1929,12 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                {"inputdir", required_argument, NULL, 3},
                {"load-language", required_argument, NULL, 4},
                {"max-connections", required_argument, NULL, 5},
-               {"multibyte", required_argument, NULL, 6},
+               {"encoding", required_argument, NULL, 6},
                {"outputdir", required_argument, NULL, 7},
                {"schedule", required_argument, NULL, 8},
                {"temp-install", required_argument, NULL, 9},
                {"no-locale", no_argument, NULL, 10},
                {"top-builddir", required_argument, NULL, 11},
-               {"temp-port", required_argument, NULL, 12},
                {"host", required_argument, NULL, 13},
                {"port", required_argument, NULL, 14},
                {"user", required_argument, NULL, 15},
@@ -1866,11 +1942,15 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                {"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},
                {NULL, 0, NULL, 0}
        };
 
        progname = get_progname(argv[0]);
-       set_pglocale_pgservice(argv[0], "pg_regress");
+       set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
 
 #ifndef HAVE_UNIX_SOCKETS
        /* no unix domain sockets available, so change default */
@@ -1932,20 +2012,12 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                        case 11:
                                top_builddir = strdup(optarg);
                                break;
-                       case 12:
-                               {
-                                       int                     p = atoi(optarg);
-
-                                       /* Since Makefile isn't very bright, check port range */
-                                       if (p >= 1024 && p <= 65535)
-                                               temp_port = p;
-                               }
-                               break;
                        case 13:
                                hostname = strdup(optarg);
                                break;
                        case 14:
                                port = atoi(optarg);
+                               port_specified_by_user = true;
                                break;
                        case 15:
                                user = strdup(optarg);
@@ -1964,6 +2036,18 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                        case 19:
                                temp_config = strdup(optarg);
                                break;
+                       case 20:
+                               use_existing = true;
+                               break;
+                       case 21:
+                               launcher = strdup(optarg);
+                               break;
+                       case 22:
+                               add_stringlist_item(&loadextension, optarg);
+                               break;
+                       case 23:
+                               add_stringlist_item(&extra_install, optarg);
+                               break;
                        default:
                                /* getopt_long already emitted a complaint */
                                fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
@@ -1981,8 +2065,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                optind++;
        }
 
-       if (temp_install)
-               port = temp_port;
+       if (temp_install && !port_specified_by_user)
+
+               /*
+                * To reduce chances of interference with parallel installations, use
+                * a port number starting in the private range (49152-65535)
+                * calculated from the version number.
+                */
+               port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
 
        inputdir = make_absolute_path(inputdir);
        outputdir = make_absolute_path(outputdir);
@@ -2001,6 +2091,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 
        if (temp_install)
        {
+               FILE       *pg_conf;
+               _stringlist *sl;
+
                /*
                 * Prepare the temp installation
                 */
@@ -2029,7 +2122,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                /* "make install" */
 #ifndef WIN32_ONLY_COMPILER
                snprintf(buf, sizeof(buf),
-                                SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install with_perl=no with_python=no > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
+                                SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
                                 makeprog, top_builddir, temp_install, outputdir);
 #else
                snprintf(buf, sizeof(buf),
@@ -2042,6 +2135,24 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                        exit_nicely(2);
                }
 
+               for (sl = extra_install; sl != NULL; sl = sl->next)
+               {
+#ifndef WIN32_ONLY_COMPILER
+                       snprintf(buf, sizeof(buf),
+                                        SYSTEMQUOTE "\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
+                                        makeprog, top_builddir, sl->str, temp_install, outputdir);
+#else
+                       fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n", progname));
+                       exit_nicely(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_nicely(2);
+                       }
+               }
+
                /* initdb */
                header(_("initializing database system"));
                snprintf(buf, sizeof(buf),
@@ -2056,20 +2167,29 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                        exit_nicely(2);
                }
 
-               /* add any extra config specified to the postgresql.conf */
+               /*
+                * 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.)
+                */
+               snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install);
+               pg_conf = fopen(buf, "a");
+               if (pg_conf == NULL)
+               {
+                       fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
+                       exit_nicely(2);
+               }
+               fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
+               fputs("max_prepared_transactions = 2\n", pg_conf);
+
                if (temp_config != NULL)
                {
                        FILE       *extra_conf;
-                       FILE       *pg_conf;
                        char            line_buf[1024];
 
-                       snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install);
-                       pg_conf = fopen(buf, "a");
-                       if (pg_conf == NULL)
-                       {
-                               fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
-                               exit_nicely(2);
-                       }
                        extra_conf = fopen(temp_config, "r");
                        if (extra_conf == NULL)
                        {
@@ -2079,7 +2199,39 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                        while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
                                fputs(line_buf, pg_conf);
                        fclose(extra_conf);
-                       fclose(pg_conf);
+               }
+
+               fclose(pg_conf);
+
+               /*
+                * Check if there is a postmaster running already.
+                */
+               snprintf(buf2, sizeof(buf2),
+                                SYSTEMQUOTE "\"%s/psql\" -X postgres <%s 2>%s" SYSTEMQUOTE,
+                                bindir, DEVNULL, DEVNULL);
+
+               for (i = 0; i < 16; i++)
+               {
+                       if (system(buf2) == 0)
+                       {
+                               char            s[16];
+
+                               if (port_specified_by_user || i == 15)
+                               {
+                                       fprintf(stderr, _("port %d apparently in use\n"), port);
+                                       if (!port_specified_by_user)
+                                               fprintf(stderr, _("%s: could not determine an available port\n"), progname);
+                                       fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n"));
+                                       exit_nicely(2);
+                               }
+
+                               fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1);
+                               port++;
+                               sprintf(s, "%d", port);
+                               doputenv("PGPORT", s);
+                       }
+                       else
+                               break;
                }
 
                /*
@@ -2105,13 +2257,10 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                 * second or so, but Cygwin is reportedly *much* slower).  Don't wait
                 * forever, however.
                 */
-               snprintf(buf, sizeof(buf),
-                                SYSTEMQUOTE "\"%s/psql\" -X postgres <%s 2>%s" SYSTEMQUOTE,
-                                bindir, DEVNULL, DEVNULL);
                for (i = 0; i < 60; i++)
                {
                        /* Done if psql succeeds */
-                       if (system(buf) == 0)
+                       if (system(buf2) == 0)
                                break;
 
                        /*
@@ -2155,8 +2304,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 
                postmaster_running = true;
 
+#ifdef WIN64
+/* need a series of two casts to convert HANDLE without compiler warning */
+#define ULONGPID(x) (unsigned long) (unsigned long long) (x)
+#else
+#define ULONGPID(x) (unsigned long) (x)
+#endif
                printf(_("running on port %d with pid %lu\n"),
-                          temp_port, (unsigned long) postmaster_pid);
+                          port, ULONGPID(postmaster_pid));
        }
        else
        {
@@ -2164,19 +2319,25 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
                 * Using an existing installation, so may need to get rid of
                 * pre-existing database(s) and role(s)
                 */
-               for (sl = dblist; sl; sl = sl->next)
-                       drop_database_if_exists(sl->str);
-               for (sl = extraroles; sl; sl = sl->next)
-                       drop_role_if_exists(sl->str);
+               if (!use_existing)
+               {
+                       for (sl = dblist; sl; sl = sl->next)
+                               drop_database_if_exists(sl->str);
+                       for (sl = extraroles; sl; sl = sl->next)
+                               drop_role_if_exists(sl->str);
+               }
        }
 
        /*
         * Create the test database(s) and role(s)
         */
-       for (sl = dblist; sl; sl = sl->next)
-               create_database(sl->str);
-       for (sl = extraroles; sl; sl = sl->next)
-               create_role(sl->str, dblist);
+       if (!use_existing)
+       {
+               for (sl = dblist; sl; sl = sl->next)
+                       create_database(sl->str);
+               for (sl = extraroles; sl; sl = sl->next)
+                       create_role(sl->str, dblist);
+       }
 
        /*
         * Ready to run the tests