*
* 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
*
*-------------------------------------------------------------------------
*/
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;
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;
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;
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.
{
/* 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);
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;
}
}
{
char testtablespace[MAXPGPATH];
char indir[MAXPGPATH];
- struct stat st;
+ struct stat st;
int ret;
char **name;
char **names;
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;
}
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
{
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);
*/
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)
{
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
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
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);
* 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;
while (tests_left > 0)
{
PID_TYPE p;
- int exit_status;
#ifndef WIN32
+ int exit_status;
+
p = wait(&exit_status);
if (p == INVALID_PID)
exit_nicely(2);
}
#else
+ DWORD exit_status;
int r;
r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
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--;
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 */
bool differ = false;
if (num_tests > 1)
- status(_(" %-20s ... "), tests[i]);
+ status(_(" %-24s ... "), tests[i]);
/*
* Advance over all three lists simultaneously.
*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);
*/
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';"
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);
}
}
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])
{
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"));
int i;
int option_index;
char buf[MAXPGPATH * 4];
+ char buf2[MAXPGPATH * 4];
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"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},
{"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 */
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);
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"),
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);
if (temp_install)
{
+ FILE *pg_conf;
+ _stringlist *sl;
+
/*
* Prepare the temp installation
*/
/* "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),
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),
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)
{
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;
}
/*
* 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;
/*
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
{
* 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