*
* This code is released under the terms of the PostgreSQL License.
*
- * Portions Copyright (c) 1996-2007, 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.32 2007/05/31 15:13:06 petere Exp $
+ * src/test/regress/pg_regress.c
*
*-------------------------------------------------------------------------
*/
-#include "postgres_fe.h"
+#include "pg_regress.h"
#include <ctype.h>
#include <sys/stat.h>
#include "getopt_long.h"
#include "pg_config_paths.h"
-#ifndef WIN32
-#define PID_TYPE pid_t
-#define INVALID_PID (-1)
-#else
-#define PID_TYPE HANDLE
-#define INVALID_PID INVALID_HANDLE_VALUE
-#endif
-
-
-/* simple list of strings */
-typedef struct _stringlist
-{
- char *str;
- struct _stringlist *next;
-} _stringlist;
-
/* for resultmap we need a list of pairs of strings */
typedef struct _resultmap
{
char *test;
+ char *type;
char *resultfile;
struct _resultmap *next;
} _resultmap;
* 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.
*/
-static char *bindir = PGBINDIR;
-static char *libdir = LIBDIR;
-static char *datadir = PGSHAREDIR;
-static char *host_platform = HOST_TUPLE;
+char *bindir = PGBINDIR;
+char *libdir = LIBDIR;
+char *datadir = PGSHAREDIR;
+char *host_platform = HOST_TUPLE;
+
#ifndef WIN32_ONLY_COMPILER
static char *makeprog = MAKEPROG;
#endif
static char *shellprog = SHELLPROG;
#endif
-/* currently we can use the same diff switches on all platforms */
-static const char *basic_diff_opts = "-w";
-static const char *pretty_diff_opts = "-w -C3";
+/*
+ * 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 */
-static char *dbname = "regression";
-static bool debug = false;
-static char *inputdir = ".";
-static char *outputdir = ".";
+_stringlist *dblist = NULL;
+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;
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 char *psqldir = NULL;
+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 char *srcdir = NULL;
+static _stringlist *extraroles = NULL;
+static _stringlist *extra_install = NULL;
/* internal variables */
static const char *progname;
static int fail_count = 0;
static int fail_ignore_count = 0;
-static bool
-directory_exists(const char *dir);
-static void
-make_directory(const char *dir);
+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(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.
*/
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
-static void
+static void
unlimit_core_size(void)
{
struct rlimit lim;
- getrlimit(RLIMIT_CORE,&lim);
+
+ getrlimit(RLIMIT_CORE, &lim);
if (lim.rlim_max == 0)
{
fprintf(stderr,
- _("%s: cannot set core size,: disallowed by hard limit.\n"),
+ _("%s: could not set core size: disallowed by hard limit\n"),
progname);
return;
}
else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
{
lim.rlim_cur = lim.rlim_max;
- setrlimit(RLIMIT_CORE,&lim);
- }
+ setrlimit(RLIMIT_CORE, &lim);
+ }
}
#endif
/*
* Add an item at the end of a stringlist.
*/
-static void
+void
add_stringlist_item(_stringlist ** listhead, const char *str)
{
_stringlist *newentry = malloc(sizeof(_stringlist));
}
}
+/*
+ * Free a stringlist.
+ */
+static void
+free_stringlist(_stringlist ** listhead)
+{
+ if (listhead == NULL || *listhead == NULL)
+ return;
+ if ((*listhead)->next != NULL)
+ free_stringlist(&((*listhead)->next));
+ free((*listhead)->str);
+ free(*listhead);
+ *listhead = NULL;
+}
+
+/*
+ * Split a delimited string into a stringlist
+ */
+static void
+split_to_stringlist(const char *s, const char *delim, _stringlist ** listhead)
+{
+ char *sc = strdup(s);
+ char *token = strtok(sc, delim);
+
+ while (token)
+ {
+ add_stringlist_item(listhead, token);
+ token = strtok(NULL, delim);
+ }
+ free(sc);
+}
+
/*
* Print a progress banner on stdout.
*/
{
/* 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;
}
}
* Always exit through here, not through plain exit(), to ensure we make
* an effort to shut down a temp postmaster
*/
-static void
+void
exit_nicely(int code)
{
stop_postmaster();
* Replace all occurances of a string in a string with a different string.
* NOTE: Assumes there is enough room in the target buffer!
*/
-static void
+void
replace_string(char *string, char *replace, char *replacement)
{
- char *ptr;
+ char *ptr;
- while ((ptr = strstr(string, replace)) != NULL)
+ while ((ptr = strstr(string, replace)) != NULL)
{
- char *dup = strdup(string);
+ char *dup = strdup(string);
strlcpy(string, dup, ptr - string + 1);
strcat(string, replacement);
* the given suffix.
*/
static void
-convert_sourcefiles_in(char *source, char *dest, char *suffix)
+convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
{
- char abs_srcdir[MAXPGPATH];
- char abs_builddir[MAXPGPATH];
- char testtablespace[MAXPGPATH];
- char indir[MAXPGPATH];
- char **name;
- char **names;
- int count = 0;
-#ifdef WIN32
- char *c;
-#endif
+ char testtablespace[MAXPGPATH];
+ char indir[MAXPGPATH];
+ struct stat st;
+ int ret;
+ char **name;
+ char **names;
+ int count = 0;
- if (!getcwd(abs_builddir, sizeof(abs_builddir)))
+ snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir);
+
+ /* Check that indir actually exists and is a directory */
+ ret = stat(indir, &st);
+ if (ret != 0 || !S_ISDIR(st.st_mode))
{
- fprintf(stderr, _("%s: could not get current directory: %s\n"),
- progname, strerror(errno));
- exit_nicely(2);
+ /*
+ * No warning, to avoid noise in tests that do not have these
+ * directories; for example, ecpg, contrib and src/pl.
+ */
+ return;
}
- /*
- * in a VPATH build, use the provided source directory; otherwise, use
- * the current directory.
- */
- if (srcdir)
- strcpy(abs_srcdir, srcdir);
- else
- strcpy(abs_srcdir, abs_builddir);
-
- snprintf(indir, MAXPGPATH, "%s/%s", abs_srcdir, source);
names = pgfnames(indir);
if (!names)
/* Error logged in pgfnames */
exit_nicely(2);
+ snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
+
#ifdef WIN32
- /* in Win32, replace backslashes with forward slashes */
- for (c = abs_builddir; *c; c++)
- if (*c == '\\')
- *c = '/';
- for (c = abs_srcdir; *c; c++)
- if (*c == '\\')
- *c = '/';
-#endif
- /* try to create the test tablespace dir if it doesn't exist */
- snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", abs_builddir);
+ /*
+ * 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.)
+ *
+ * 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
+ * Windows. See pgsql-hackers discussion of 2008-01-18.
+ */
if (directory_exists(testtablespace))
rmtree(testtablespace, true);
make_directory(testtablespace);
+#endif
/* finally loop on each file and do the replacement */
for (name = names; *name; name++)
{
- char srcfile[MAXPGPATH];
- char destfile[MAXPGPATH];
- char prefix[MAXPGPATH];
- FILE *infile,
- *outfile;
- char line[1024];
+ char srcfile[MAXPGPATH];
+ char destfile[MAXPGPATH];
+ char prefix[MAXPGPATH];
+ FILE *infile,
+ *outfile;
+ char line[1024];
/* reject filenames not finishing in ".source" */
if (strlen(*name) < 8)
/* build the full actual paths to open */
snprintf(prefix, strlen(*name) - 6, "%s", *name);
snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name);
- snprintf(destfile, MAXPGPATH, "%s/%s.%s", dest, prefix, suffix);
+ snprintf(destfile, MAXPGPATH, "%s/%s.%s", dest_subdir, prefix, suffix);
infile = fopen(srcfile, "r");
if (!infile)
if (!outfile)
{
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
- progname, destfile, strerror(errno));
+ progname, destfile, strerror(errno));
exit_nicely(2);
}
while (fgets(line, sizeof(line), infile))
{
- replace_string(line, "@abs_srcdir@", abs_srcdir);
- replace_string(line, "@abs_builddir@", abs_builddir);
+ replace_string(line, "@abs_srcdir@", inputdir);
+ replace_string(line, "@abs_builddir@", outputdir);
replace_string(line, "@testtablespace@", testtablespace);
+ replace_string(line, "@libdir@", dlpath);
replace_string(line, "@DLSUFFIX@", DLSUFFIX);
fputs(line, outfile);
}
/*
* If we didn't process any files, complain because it probably means
- * somebody neglected to pass the needed --srcdir argument.
+ * somebody neglected to pass the needed --inputdir argument.
*/
if (count <= 0)
{
- fprintf(stderr, _("%s: no *.source files found in %s\n"),
+ fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"),
progname, indir);
exit_nicely(2);
}
-
- pgfnames_cleanup(names);
+
+ pgfnames_cleanup(names);
}
/* Create the .sql and .out files from the .source files, if any */
static void
convert_sourcefiles(void)
{
- struct stat st;
- int ret;
-
- ret = stat("input", &st);
- if (ret == 0 && S_ISDIR(st.st_mode))
- convert_sourcefiles_in("input", "sql", "sql");
-
- ret = stat("output", &st);
- if (ret == 0 && S_ISDIR(st.st_mode))
- convert_sourcefiles_in("output", "expected", "out");
+ convert_sourcefiles_in("input", "sql", "sql");
+ convert_sourcefiles_in("output", "expected", "out");
}
/*
while (fgets(buf, sizeof(buf), f))
{
char *platform;
+ char *file_type;
char *expected;
int i;
buf[--i] = '\0';
/* parse out the line fields */
- platform = strchr(buf, '/');
+ file_type = strchr(buf, ':');
+ if (!file_type)
+ {
+ fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
+ buf);
+ exit_nicely(2);
+ }
+ *file_type++ = '\0';
+
+ platform = strchr(file_type, ':');
if (!platform)
{
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
_resultmap *entry = malloc(sizeof(_resultmap));
entry->test = strdup(buf);
+ entry->type = strdup(file_type);
entry->resultfile = strdup(expected);
entry->next = resultmap;
resultmap = entry;
fclose(f);
}
+/*
+ * Check in resultmap if we should be looking at a different file
+ */
+static
+const char *
+get_expectfile(const char *testname, const char *file)
+{
+ char *file_type;
+ _resultmap *rm;
+
+ /*
+ * Determine the file type from the file name. This is just what is
+ * following the last dot in the file name.
+ */
+ if (!file || !(file_type = strrchr(file, '.')))
+ return NULL;
+
+ file_type++;
+
+ for (rm = resultmap; rm != NULL; rm = rm->next)
+ {
+ if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
+ {
+ return rm->resultfile;
+ }
+ }
+
+ return NULL;
+}
+
/*
* Handy subroutine for setting an environment variable "var" to "val"
*/
{
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");
+ /*
+ * 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
*
* Returns the process ID (or HANDLE) so we can wait for it later
*/
-static PID_TYPE
+PID_TYPE
spawn_process(const char *cmdline)
{
#ifndef WIN32
char *cmdline2 = malloc(strlen(cmdline) + 6);
sprintf(cmdline2, "exec %s", cmdline);
- execl(shellprog, shellprog, "-c", cmdline2, NULL);
+ execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
progname, shellprog, strerror(errno));
exit(1); /* not exit_nicely here... */
return pid;
#else
char *cmdline2;
- BOOL b;
+ BOOL b;
STARTUPINFO si;
PROCESS_INFORMATION pi;
- HANDLE origToken;
- HANDLE restrictedToken;
+ HANDLE origToken;
+ HANDLE restrictedToken;
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
SID_AND_ATTRIBUTES dropSids[2];
__CreateRestrictedToken _CreateRestrictedToken = NULL;
- HANDLE Advapi32Handle;
+ 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, "ERROR: cannot create restricted tokens on this platform\n");
- exit_nicely(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: %lu\n", GetLastError());
- exit_nicely(2);
- }
+ _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_nicely(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: %lu\n"),
+ GetLastError());
+ exit_nicely(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: %lu\n", GetLastError());
- exit_nicely(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: %lu\n"), GetLastError());
+ exit_nicely(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: %lu\n", GetLastError());
- exit_nicely(2);
- }
+ 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: %lu\n"),
+ GetLastError());
+ exit_nicely(2);
+ }
cmdline2 = malloc(strlen(cmdline) + 8);
sprintf(cmdline2, "cmd /c %s", cmdline);
- if (!CreateProcessAsUser(restrictedToken, NULL, cmdline2, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
+#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\": %lu\n"),
cmdline2, GetLastError());
exit_nicely(2);
}
+
free(cmdline2);
+ ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
return pi.hProcess;
#endif
}
-/*
- * start a psql test process for specified file (including redirection),
- * and return process ID
- */
-static PID_TYPE
-psql_start_test(const char *testname)
-{
- PID_TYPE pid;
- char infile[MAXPGPATH];
- char outfile[MAXPGPATH];
- char psql_cmd[MAXPGPATH * 3];
-
- snprintf(infile, sizeof(infile), "%s/sql/%s.sql",
- inputdir, testname);
- snprintf(outfile, sizeof(outfile), "%s/results/%s.out",
- outputdir, testname);
-
- snprintf(psql_cmd, sizeof(psql_cmd),
- SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
- psqldir ? psqldir : "",
- psqldir ? "/" : "",
- dbname,
- infile,
- outfile);
-
- pid = spawn_process(psql_cmd);
-
- if (pid == INVALID_PID)
- {
- fprintf(stderr, _("could not start process for test %s\n"),
- testname);
- exit_nicely(2);
- }
-
- return pid;
-}
-
/*
* Count bytes in file
*/
return l;
}
-static bool
+bool
file_exists(const char *file)
{
FILE *f = fopen(file, "r");
if (stat(dir, &st) != 0)
return false;
- if (st.st_mode & S_IFDIR)
+ if (S_ISDIR(st.st_mode))
return true;
return false;
}
}
}
+/*
+ * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
+ */
+static char *
+get_alternative_expectfile(const char *expectfile, int i)
+{
+ char *last_dot;
+ int ssize = strlen(expectfile) + 2 + 1;
+ char *tmp = (char *) malloc(ssize);
+ char *s = (char *) malloc(ssize);
+
+ 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);
+ return s;
+}
+
/*
* Run a "diff" command and also check that it didn't crash
*/
* In the true case, the diff is appended to the diffs file.
*/
static bool
-results_differ(const char *testname)
+results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
{
- const char *expectname;
- char resultsfile[MAXPGPATH];
char expectfile[MAXPGPATH];
char diff[MAXPGPATH];
char cmd[MAXPGPATH * 3];
char best_expect_file[MAXPGPATH];
- _resultmap *rm;
FILE *difffile;
int best_line_count;
int i;
int l;
+ const char *platform_expectfile;
- /* Check in resultmap if we should be looking at a different file */
- expectname = testname;
- for (rm = resultmap; rm != NULL; rm = rm->next)
- {
- if (strcmp(testname, rm->test) == 0)
- {
- expectname = rm->resultfile;
- break;
- }
- }
+ /*
+ * We can pass either the resultsfile or the expectfile, they should have
+ * the same type (filename.type) anyway.
+ */
+ platform_expectfile = get_expectfile(testname, resultsfile);
- /* Name of test results file */
- snprintf(resultsfile, sizeof(resultsfile), "%s/results/%s.out",
- outputdir, testname);
+ strcpy(expectfile, default_expectfile);
+ if (platform_expectfile)
+ {
+ /*
+ * Replace everything afer the last slash in expectfile with what the
+ * platform_expectfile contains.
+ */
+ char *p = strrchr(expectfile, '/');
- /* Name of expected-results file */
- snprintf(expectfile, sizeof(expectfile), "%s/expected/%s.out",
- inputdir, expectname);
+ if (p)
+ strcpy(++p, platform_expectfile);
+ }
/* Name to use for temporary diff file */
- snprintf(diff, sizeof(diff), "%s/results/%s.diff",
- outputdir, testname);
+ snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
/* OK, run the diff */
snprintf(cmd, sizeof(cmd),
for (i = 0; i <= 9; i++)
{
- snprintf(expectfile, sizeof(expectfile), "%s/expected/%s_%d.out",
- inputdir, expectname, i);
- if (!file_exists(expectfile))
+ char *alt_expectfile;
+
+ alt_expectfile = get_alternative_expectfile(expectfile, i);
+ if (!file_exists(alt_expectfile))
continue;
snprintf(cmd, sizeof(cmd),
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
- basic_diff_opts, expectfile, resultsfile, diff);
+ basic_diff_opts, alt_expectfile, resultsfile, diff);
if (run_diff(cmd, diff) == 0)
{
{
/* This diff was a better match than the last one */
best_line_count = l;
- strcpy(best_expect_file, expectfile);
+ strcpy(best_expect_file, alt_expectfile);
}
+ free(alt_expectfile);
}
/*
* haven't found a complete match yet.
*/
- if (strcmp(expectname, testname) != 0)
+ if (platform_expectfile)
{
- snprintf(expectfile, sizeof(expectfile), "%s/expected/%s.out",
- inputdir, testname);
-
snprintf(cmd, sizeof(cmd),
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
- basic_diff_opts, expectfile, resultsfile, diff);
+ basic_diff_opts, default_expectfile, resultsfile, diff);
if (run_diff(cmd, diff) == 0)
{
{
/* This diff was a better match than the last one */
best_line_count = l;
- strcpy(best_expect_file, expectfile);
+ strcpy(best_expect_file, default_expectfile);
}
}
}
/*
- * Wait for specified subprocesses to finish
+ * Wait for specified subprocesses to finish, and return their exit
+ * statuses into statuses[]
*
- * If names isn't NULL, report each subprocess as it finishes
+ * 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, char **names, int num_tests)
+wait_for_tests(PID_TYPE * pids, int *statuses, char **names, int num_tests)
{
int tests_left;
int i;
PID_TYPE p;
#ifndef WIN32
- p = wait(NULL);
+ 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);
if (p == pids[i])
{
#ifdef WIN32
+ GetExitCodeProcess(pids[i], &exit_status);
CloseHandle(pids[i]);
#endif
pids[i] = INVALID_PID;
+ statuses[i] = (int) exit_status;
if (names)
status(" %s", names[i]);
tests_left--;
#endif
}
+/*
+ * report nonzero exit code from a test process
+ */
+static void
+log_child_failure(int exitstatus)
+{
+ if (WIFEXITED(exitstatus))
+ status(_(" (test process exited with exit code %d)"),
+ WEXITSTATUS(exitstatus));
+ else if (WIFSIGNALED(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));
+#endif
+ }
+ else
+ status(_(" (test process exited with unrecognized status %d)"),
+ exitstatus);
+}
+
/*
* Run all the tests specified in one schedule file
*/
static void
-run_schedule(const char *schedule)
+run_schedule(const char *schedule, test_function tfunc)
{
#define MAX_PARALLEL_TESTS 100
char *tests[MAX_PARALLEL_TESTS];
+ _stringlist *resultfiles[MAX_PARALLEL_TESTS];
+ _stringlist *expectfiles[MAX_PARALLEL_TESTS];
+ _stringlist *tags[MAX_PARALLEL_TESTS];
PID_TYPE pids[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);
+
scf = fopen(schedule, "r");
if (!scf)
{
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]))
if (num_tests == 1)
{
- status(_("test %-20s ... "), tests[0]);
- pids[0] = psql_start_test(tests[0]);
- wait_for_tests(pids, NULL, 1);
+ 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 */
}
else if (max_connections > 0 && max_connections < num_tests)
{
if (i - oldest >= max_connections)
{
- wait_for_tests(pids + oldest, tests + oldest, i - oldest);
+ wait_for_tests(pids + oldest, statuses + oldest,
+ tests + oldest, i - oldest);
oldest = i;
}
- pids[i] = psql_start_test(tests[i]);
+ pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
}
- wait_for_tests(pids + oldest, tests + oldest, i - oldest);
+ wait_for_tests(pids + oldest, statuses + oldest,
+ tests + oldest, i - oldest);
status_end();
}
else
status(_("parallel group (%d tests): "), num_tests);
for (i = 0; i < num_tests; i++)
{
- pids[i] = psql_start_test(tests[i]);
+ pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
}
- wait_for_tests(pids, tests, num_tests);
+ wait_for_tests(pids, statuses, tests, num_tests);
status_end();
}
/* Check results for all tests */
for (i = 0; i < num_tests; i++)
{
+ _stringlist *rl,
+ *el,
+ *tl;
+ bool differ = false;
+
if (num_tests > 1)
- status(_(" %-20s ... "), tests[i]);
+ status(_(" %-24s ... "), tests[i]);
- if (results_differ(tests[i]))
+ /*
+ * Advance over all three lists simultaneously.
+ *
+ * Compare resultfiles[j] with expectfiles[j] always. Tags are
+ * optional but if there are tags, the tag list has the same
+ * length as the other two lists.
+ */
+ 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)
+ {
+ 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)
+ {
+ printf("%s ", tl->str);
+ }
+ differ |= newdiff;
+ }
+
+ if (differ)
{
bool ignore = false;
_stringlist *sl;
success_count++;
}
+ if (statuses[i] != 0)
+ log_child_failure(statuses[i]);
+
status_end();
}
}
* Run a single test
*/
static void
-run_single_test(const char *test)
+run_single_test(const char *test, test_function tfunc)
{
PID_TYPE pid;
+ int exit_status;
+ _stringlist *resultfiles = NULL;
+ _stringlist *expectfiles = NULL;
+ _stringlist *tags = NULL;
+ _stringlist *rl,
+ *el,
+ *tl;
+ bool differ = false;
+
+ status(_("test %-24s ... "), test);
+ pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
+ wait_for_tests(&pid, &exit_status, NULL, 1);
- status(_("test %-20s ... "), test);
- pid = psql_start_test(test);
- wait_for_tests(&pid, NULL, 1);
+ /*
+ * Advance over all three lists simultaneously.
+ *
+ * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
+ * but if there are tags, the tag list has the same length as the other
+ * two lists.
+ */
+ for (rl = resultfiles, el = expectfiles, tl = tags;
+ rl != NULL; /* rl and el have the same length */
+ rl = rl->next, el = el->next)
+ {
+ bool newdiff;
+
+ if (tl)
+ tl = tl->next; /* tl has the same length as rl and el if it
+ * exists */
- if (results_differ(test))
+ newdiff = results_differ(test, rl->str, el->str);
+ if (newdiff && tl)
+ {
+ printf("%s ", tl->str);
+ }
+ differ |= newdiff;
+ }
+
+ if (differ)
{
status(_("FAILED"));
fail_count++;
status(_("ok"));
success_count++;
}
+
+ if (exit_status != 0)
+ log_child_failure(exit_status);
+
status_end();
}
make_directory(file);
}
+static void
+drop_database_if_exists(const char *dbname)
+{
+ header(_("dropping database \"%s\""), dbname);
+ psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
+}
+
+static void
+create_database(const char *dbname)
+{
+ _stringlist *sl;
+
+ /*
+ * We use template0 so that any installation-local cruft in template1 will
+ * not mess up the tests.
+ */
+ header(_("creating database \"%s\""), dbname);
+ if (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%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';"
+ "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);
+
+ /*
+ * 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 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 void
+drop_role_if_exists(const char *rolename)
+{
+ header(_("dropping role \"%s\""), rolename);
+ psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
+}
+
+static void
+create_role(const char *rolename, const _stringlist * granted_dbs)
+{
+ header(_("creating role \"%s\""), rolename);
+ psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
+ for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
+ {
+ psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
+ granted_dbs->str, rolename);
+ }
+}
+
+static char *
+make_absolute_path(const char *in)
+{
+ char *result;
+
+ if (is_absolute_path(in))
+ result = strdup(in);
+ else
+ {
+ static char cwdbuf[MAXPGPATH];
+
+ if (!cwdbuf[0])
+ {
+ if (!getcwd(cwdbuf, sizeof(cwdbuf)))
+ {
+ fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
+ exit_nicely(2);
+ }
+ }
+
+ result = malloc(strlen(cwdbuf) + strlen(in) + 2);
+ sprintf(result, "%s/%s", cwdbuf, in);
+ }
+
+ canonicalize_path(result);
+ return result;
+}
+
static void
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(_(" --srcdir=DIR absolute path to source directory (for VPATH builds)\n"));
+ printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
- printf(_(" --no-locale use C locale\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
-main(int argc, char *argv[])
+regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
{
_stringlist *sl;
int c;
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},
{"psqldir", required_argument, NULL, 16},
- {"srcdir", required_argument, NULL, 17},
+ {"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 */
hostname = "localhost";
#endif
+ /*
+ * We call the initialization function here because that way we can set
+ * default parameters and let them be overwritten by the commandline.
+ */
+ ifunc();
+
while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
{
switch (c)
help();
exit_nicely(0);
case 'V':
- printf("pg_regress (PostgreSQL %s)\n", PG_VERSION);
+ puts("pg_regress (PostgreSQL) " PG_VERSION);
exit_nicely(0);
case 1:
- dbname = strdup(optarg);
+
+ /*
+ * If a default database was specified, we need to remove it
+ * before we add the specified one.
+ */
+ free_stringlist(&dblist);
+ split_to_stringlist(strdup(optarg), ", ", &dblist);
break;
case 2:
debug = true;
add_stringlist_item(&schedulelist, optarg);
break;
case 9:
- /* temp_install must be absolute path */
- if (is_absolute_path(optarg))
- temp_install = strdup(optarg);
- else
- {
- char cwdbuf[MAXPGPATH];
-
- if (!getcwd(cwdbuf, sizeof(cwdbuf)))
- {
- fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
- exit_nicely(2);
- }
- temp_install = malloc(strlen(cwdbuf) + strlen(optarg) + 2);
- sprintf(temp_install, "%s/%s", cwdbuf, optarg);
- }
- canonicalize_path(temp_install);
+ temp_install = make_absolute_path(optarg);
break;
case 10:
nolocale = true;
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);
psqldir = strdup(optarg);
break;
case 17:
- srcdir = strdup(optarg);
+ dlpath = strdup(optarg);
+ break;
+ case 18:
+ split_to_stringlist(strdup(optarg), ", ", &extraroles);
+ break;
+ 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 */
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);
+ dlpath = make_absolute_path(dlpath);
/*
* Initialization
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),
- SYSTEMQUOTE "perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
- top_builddir, temp_install, outputdir);
+ snprintf(buf, sizeof(buf),
+ SYSTEMQUOTE "perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
+ top_builddir, temp_install, outputdir);
#endif
if (system(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);
}
+ /*
+ * 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;
+ char line_buf[1024];
+
+ extra_conf = fopen(temp_config, "r");
+ if (extra_conf == NULL)
+ {
+ fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno));
+ exit_nicely(2);
+ }
+ while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
+ fputs(line_buf, pg_conf);
+ fclose(extra_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;
+ }
+
/*
* Start the temp postmaster
*/
* 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.
+ * pre-existing database(s) and role(s)
*/
- header(_("dropping database \"%s\""), dbname);
- psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
+ 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
- *
- * We use template0 so that any installation-local cruft in template1 will
- * not mess up the tests.
- */
- header(_("creating database \"%s\""), dbname);
- if (encoding)
- psql_command("postgres",
- "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'",
- dbname, encoding);
- else
- /* use installation default */
- psql_command("postgres",
- "CREATE DATABASE \"%s\" TEMPLATE=template0",
- dbname);
-
- psql_command(dbname,
- "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
- "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);
-
- /*
- * Install any requested PL languages
+ * Create the test database(s) and role(s)
*/
- for (sl = loadlanguage; sl != NULL; sl = sl->next)
+ if (!use_existing)
{
- header(_("installing %s"), sl->str);
- psql_command(dbname, "CREATE LANGUAGE \"%s\"", sl->str);
+ for (sl = dblist; sl; sl = sl->next)
+ create_database(sl->str);
+ for (sl = extraroles; sl; sl = sl->next)
+ create_role(sl->str, dblist);
}
/*
for (sl = schedulelist; sl != NULL; sl = sl->next)
{
- run_schedule(sl->str);
+ run_schedule(sl->str, tfunc);
}
for (sl = extra_tests; sl != NULL; sl = sl->next)
{
- run_single_test(sl->str);
+ run_single_test(sl->str, tfunc);
}
/*