2006-07-19 04:37:00 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_regress --- regression test driver
|
|
|
|
*
|
|
|
|
* This is a C implementation of the previous shell script for running
|
2006-07-21 02:24:04 +02:00
|
|
|
* the regression tests, and should be mostly compatible with it.
|
2006-07-19 04:37:00 +02:00
|
|
|
* Initial author of C translation: Magnus Hagander
|
|
|
|
*
|
|
|
|
* This code is released under the terms of the PostgreSQL License.
|
|
|
|
*
|
2019-01-02 18:44:25 +01:00
|
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
2006-07-19 04:37:00 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/test/regress/pg_regress.c
|
2006-07-19 04:37:00 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2017-03-09 02:41:06 +01:00
|
|
|
#include "postgres_fe.h"
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
2006-07-19 07:21:57 +02:00
|
|
|
#include <signal.h>
|
2006-07-19 04:37:00 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2007-01-05 17:17:55 +01:00
|
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#endif
|
|
|
|
|
2017-03-09 02:41:06 +01:00
|
|
|
#include "pg_regress.h"
|
|
|
|
|
2019-05-14 20:19:49 +02:00
|
|
|
#include "common/logging.h"
|
2015-03-30 23:07:52 +02:00
|
|
|
#include "common/restricted_token.h"
|
2014-12-18 04:48:40 +01:00
|
|
|
#include "common/username.h"
|
2006-07-19 04:37:00 +02:00
|
|
|
#include "getopt_long.h"
|
2014-06-14 15:41:13 +02:00
|
|
|
#include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
|
2006-07-20 03:16:57 +02:00
|
|
|
#include "pg_config_paths.h"
|
2019-02-10 22:54:31 +01:00
|
|
|
#include "portability/instr_time.h"
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* for resultmap we need a list of pairs of strings */
|
|
|
|
typedef struct _resultmap
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *test;
|
2007-06-12 13:07:34 +02:00
|
|
|
char *type;
|
2006-10-04 02:30:14 +02:00
|
|
|
char *resultfile;
|
2006-07-19 04:37:00 +02:00
|
|
|
struct _resultmap *next;
|
2014-05-06 18:12:18 +02:00
|
|
|
} _resultmap;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
2015-04-23 14:59:52 +02:00
|
|
|
* Values obtained from Makefile.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
2007-11-15 22:14:46 +01:00
|
|
|
char *host_platform = HOST_TUPLE;
|
|
|
|
|
2006-07-20 04:15:17 +02:00
|
|
|
#ifndef WIN32 /* not used in WIN32 case */
|
2006-07-19 19:02:59 +02:00
|
|
|
static char *shellprog = SHELLPROG;
|
2006-07-20 04:15:17 +02:00
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2009-11-23 17:02:24 +01:00
|
|
|
/*
|
|
|
|
* On Windows we use -w in diff switches to avoid problems with inconsistent
|
2014-05-06 18:12:18 +02:00
|
|
|
* newline representation. The actual result files will generally have
|
2009-11-23 17:02:24 +01:00
|
|
|
* Windows-style newlines, but the comparison files might or might not.
|
|
|
|
*/
|
|
|
|
#ifndef WIN32
|
2009-11-22 18:54:23 +01:00
|
|
|
const char *basic_diff_opts = "";
|
2019-01-02 21:20:53 +01:00
|
|
|
const char *pretty_diff_opts = "-U3";
|
2009-11-23 17:02:24 +01:00
|
|
|
#else
|
|
|
|
const char *basic_diff_opts = "-w";
|
2019-01-02 21:20:53 +01:00
|
|
|
const char *pretty_diff_opts = "-w -U3";
|
2009-11-23 17:02:24 +01:00
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* options settable from command line */
|
2007-06-12 13:07:34 +02:00
|
|
|
_stringlist *dblist = NULL;
|
2007-11-15 22:14:46 +01:00
|
|
|
bool debug = false;
|
|
|
|
char *inputdir = ".";
|
|
|
|
char *outputdir = ".";
|
2015-04-23 14:59:52 +02:00
|
|
|
char *bindir = PGBINDIR;
|
2011-01-24 02:44:48 +01:00
|
|
|
char *launcher = NULL;
|
2006-07-19 04:37:00 +02:00
|
|
|
static _stringlist *loadlanguage = NULL;
|
2011-03-05 03:51:14 +01:00
|
|
|
static _stringlist *loadextension = NULL;
|
2006-10-04 02:30:14 +02:00
|
|
|
static int max_connections = 0;
|
2017-10-07 23:20:09 +02:00
|
|
|
static int max_concurrent_tests = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
static char *encoding = NULL;
|
|
|
|
static _stringlist *schedulelist = NULL;
|
|
|
|
static _stringlist *extra_tests = NULL;
|
2015-04-23 14:59:52 +02:00
|
|
|
static char *temp_instance = NULL;
|
2016-02-28 15:38:43 +01:00
|
|
|
static _stringlist *temp_configs = NULL;
|
2006-07-19 04:37:00 +02:00
|
|
|
static bool nolocale = false;
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 02:32:45 +01:00
|
|
|
static bool use_existing = false;
|
2006-07-19 04:37:00 +02:00
|
|
|
static char *hostname = NULL;
|
2006-10-04 02:30:14 +02:00
|
|
|
static int port = -1;
|
2008-11-28 13:45:34 +01:00
|
|
|
static bool port_specified_by_user = false;
|
2008-10-02 00:38:57 +02:00
|
|
|
static char *dlpath = PKGLIBDIR;
|
2006-07-19 04:37:00 +02:00
|
|
|
static char *user = NULL;
|
2007-06-12 13:07:34 +02:00
|
|
|
static _stringlist *extraroles = NULL;
|
2014-12-18 04:48:40 +01:00
|
|
|
static char *config_auth_datadir = NULL;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* internal variables */
|
|
|
|
static const char *progname;
|
|
|
|
static char *logfilename;
|
|
|
|
static FILE *logfile;
|
|
|
|
static char *difffilename;
|
2014-06-14 15:41:13 +02:00
|
|
|
static const char *sockdir;
|
|
|
|
#ifdef HAVE_UNIX_SOCKETS
|
|
|
|
static const char *temp_sockdir;
|
|
|
|
static char sockself[MAXPGPATH];
|
|
|
|
static char socklock[MAXPGPATH];
|
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
static _resultmap *resultmap = NULL;
|
|
|
|
|
|
|
|
static PID_TYPE postmaster_pid = INVALID_PID;
|
|
|
|
static bool postmaster_running = false;
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
static int success_count = 0;
|
|
|
|
static int fail_count = 0;
|
|
|
|
static int fail_ignore_count = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2008-01-19 18:43:42 +01:00
|
|
|
static bool directory_exists(const char *dir);
|
|
|
|
static void make_directory(const char *dir);
|
2007-01-19 17:42:24 +01:00
|
|
|
|
2015-03-26 19:03:19 +01:00
|
|
|
static void header(const char *fmt,...) pg_attribute_printf(1, 2);
|
|
|
|
static void status(const char *fmt,...) pg_attribute_printf(1, 2);
|
|
|
|
static void psql_command(const char *database, const char *query,...) pg_attribute_printf(2, 3);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2007-01-05 17:17:55 +01:00
|
|
|
/*
|
|
|
|
* allow core files if possible.
|
|
|
|
*/
|
|
|
|
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
|
2007-11-15 22:14:46 +01:00
|
|
|
static void
|
2007-01-05 17:17:55 +01:00
|
|
|
unlimit_core_size(void)
|
|
|
|
{
|
|
|
|
struct rlimit lim;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
|
|
|
getrlimit(RLIMIT_CORE, &lim);
|
2007-01-05 17:17:55 +01:00
|
|
|
if (lim.rlim_max == 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
2008-08-05 07:16:08 +02:00
|
|
|
_("%s: could not set core size: disallowed by hard limit\n"),
|
2007-01-05 17:17:55 +01:00
|
|
|
progname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
|
|
|
|
{
|
|
|
|
lim.rlim_cur = lim.rlim_max;
|
2007-11-15 22:14:46 +01:00
|
|
|
setrlimit(RLIMIT_CORE, &lim);
|
|
|
|
}
|
2007-01-05 17:17:55 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an item at the end of a stringlist.
|
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
void
|
2014-05-06 18:12:18 +02:00
|
|
|
add_stringlist_item(_stringlist **listhead, const char *str)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2016-08-31 00:22:43 +02:00
|
|
|
_stringlist *newentry = pg_malloc(sizeof(_stringlist));
|
2006-07-19 04:37:00 +02:00
|
|
|
_stringlist *oldentry;
|
|
|
|
|
2016-08-31 00:22:43 +02:00
|
|
|
newentry->str = pg_strdup(str);
|
2006-07-19 04:37:00 +02:00
|
|
|
newentry->next = NULL;
|
|
|
|
if (*listhead == NULL)
|
|
|
|
*listhead = newentry;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
|
2006-10-04 02:30:14 +02:00
|
|
|
/* skip */ ;
|
2006-07-19 04:37:00 +02:00
|
|
|
oldentry->next = newentry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Free a stringlist.
|
|
|
|
*/
|
|
|
|
static void
|
2014-05-06 18:12:18 +02:00
|
|
|
free_stringlist(_stringlist **listhead)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
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
|
2014-05-06 18:12:18 +02:00
|
|
|
split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2016-08-31 00:22:43 +02:00
|
|
|
char *sc = pg_strdup(s);
|
2007-11-15 22:14:46 +01:00
|
|
|
char *token = strtok(sc, delim);
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
while (token)
|
|
|
|
{
|
|
|
|
add_stringlist_item(listhead, token);
|
|
|
|
token = strtok(NULL, delim);
|
|
|
|
}
|
|
|
|
free(sc);
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Print a progress banner on stdout.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
header(const char *fmt,...)
|
|
|
|
{
|
|
|
|
char tmp[64];
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(tmp, sizeof(tmp), fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
fprintf(stdout, "============== %-38s ==============\n", tmp);
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print "doing something ..." --- supplied text should not end with newline
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
status(const char *fmt,...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stdout, fmt, ap);
|
|
|
|
fflush(stdout);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (logfile)
|
|
|
|
{
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(logfile, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Done "doing something ..."
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
status_end(void)
|
|
|
|
{
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
fflush(stdout);
|
|
|
|
if (logfile)
|
|
|
|
fprintf(logfile, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shut down temp postmaster
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
stop_postmaster(void)
|
|
|
|
{
|
|
|
|
if (postmaster_running)
|
|
|
|
{
|
|
|
|
/* We use pg_ctl to issue the kill and wait for stop */
|
2006-10-04 02:30:14 +02:00
|
|
|
char buf[MAXPGPATH * 2];
|
2008-11-20 16:03:39 +01:00
|
|
|
int r;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-20 05:30:58 +02:00
|
|
|
/* On Windows, system() seems not to force fflush, so... */
|
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
2017-01-13 18:00:00 +01:00
|
|
|
"\"%s%spg_ctl\" stop -D \"%s/data\" -s",
|
2015-04-23 14:59:52 +02:00
|
|
|
bindir ? bindir : "",
|
|
|
|
bindir ? "/" : "",
|
|
|
|
temp_instance);
|
2008-11-25 12:49:35 +01:00
|
|
|
r = system(buf);
|
|
|
|
if (r != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
|
|
|
|
progname, r);
|
2012-01-02 21:08:04 +01:00
|
|
|
_exit(2); /* not exit(), that could be recursive */
|
2008-11-25 12:49:35 +01:00
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
postmaster_running = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-14 15:41:13 +02:00
|
|
|
#ifdef HAVE_UNIX_SOCKETS
|
|
|
|
/*
|
|
|
|
* Remove the socket temporary directory. pg_regress never waits for a
|
|
|
|
* postmaster exit, so it is indeterminate whether the postmaster has yet to
|
|
|
|
* unlink the socket and lock file. Unlink them here so we can proceed to
|
|
|
|
* remove the directory. Ignore errors; leaking a temporary directory is
|
|
|
|
* unimportant. This can run from a signal handler. The code is not
|
|
|
|
* acceptable in a Windows signal handler (see initdb.c:trapsig()), but
|
|
|
|
* Windows is not a HAVE_UNIX_SOCKETS platform.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
remove_temp(void)
|
|
|
|
{
|
|
|
|
Assert(temp_sockdir);
|
|
|
|
unlink(sockself);
|
|
|
|
unlink(socklock);
|
|
|
|
rmdir(temp_sockdir);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Signal handler that calls remove_temp() and reraises the signal.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
signal_remove_temp(int signum)
|
|
|
|
{
|
|
|
|
remove_temp();
|
|
|
|
|
|
|
|
pqsignal(signum, SIG_DFL);
|
|
|
|
raise(signum);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a temporary directory suitable for the server's Unix-domain socket.
|
|
|
|
* The directory will have mode 0700 or stricter, so no other OS user can open
|
|
|
|
* our socket to exploit our use of trust authentication. Most systems
|
|
|
|
* constrain the length of socket paths well below _POSIX_PATH_MAX, so we
|
|
|
|
* place the directory under /tmp rather than relative to the possibly-deep
|
|
|
|
* current working directory.
|
|
|
|
*
|
|
|
|
* Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits
|
|
|
|
* testing to work in builds that relocate it to a directory not writable to
|
|
|
|
* the build/test user.
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
make_temp_sockdir(void)
|
|
|
|
{
|
2016-08-31 00:22:43 +02:00
|
|
|
char *template = pg_strdup("/tmp/pg_regress-XXXXXX");
|
2014-06-14 15:41:13 +02:00
|
|
|
|
|
|
|
temp_sockdir = mkdtemp(template);
|
|
|
|
if (temp_sockdir == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
|
|
|
|
progname, template, strerror(errno));
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stage file names for remove_temp(). Unsafe in a signal handler. */
|
|
|
|
UNIXSOCK_PATH(sockself, port, temp_sockdir);
|
|
|
|
snprintf(socklock, sizeof(socklock), "%s.lock", sockself);
|
|
|
|
|
|
|
|
/* Remove the directory during clean exit. */
|
|
|
|
atexit(remove_temp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the directory before dying to the usual signals. Omit SIGQUIT,
|
|
|
|
* preserving it as a quick, untidy exit.
|
|
|
|
*/
|
|
|
|
pqsignal(SIGHUP, signal_remove_temp);
|
|
|
|
pqsignal(SIGINT, signal_remove_temp);
|
|
|
|
pqsignal(SIGPIPE, signal_remove_temp);
|
|
|
|
pqsignal(SIGTERM, signal_remove_temp);
|
|
|
|
|
|
|
|
return temp_sockdir;
|
|
|
|
}
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
|
|
|
#endif /* HAVE_UNIX_SOCKETS */
|
2014-06-14 15:41:13 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Check whether string matches pattern
|
|
|
|
*
|
|
|
|
* In the original shell script, this function was implemented using expr(1),
|
|
|
|
* which provides basic regular expressions restricted to match starting at
|
|
|
|
* the string start (in conventional regex terms, there's an implicit "^"
|
|
|
|
* at the start of the pattern --- but no implicit "$" at the end).
|
|
|
|
*
|
|
|
|
* For now, we only support "." and ".*" as non-literal metacharacters,
|
|
|
|
* because that's all that anyone has found use for in resultmap. This
|
|
|
|
* code could be extended if more functionality is needed.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
string_matches_pattern(const char *str, const char *pattern)
|
|
|
|
{
|
|
|
|
while (*str && *pattern)
|
|
|
|
{
|
|
|
|
if (*pattern == '.' && pattern[1] == '*')
|
|
|
|
{
|
|
|
|
pattern += 2;
|
|
|
|
/* Trailing .* matches everything. */
|
|
|
|
if (*pattern == '\0')
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, scan for a text position at which we can match the
|
|
|
|
* rest of the pattern.
|
|
|
|
*/
|
|
|
|
while (*str)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Optimization to prevent most recursion: don't recurse
|
|
|
|
* unless first pattern char might match this text char.
|
|
|
|
*/
|
|
|
|
if (*str == *pattern || *pattern == '.')
|
|
|
|
{
|
|
|
|
if (string_matches_pattern(str, pattern))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* End of text with no match.
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (*pattern != '.' && *str != *pattern)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Not the single-character wildcard and no explicit match? Then
|
|
|
|
* time to quit...
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
str++;
|
|
|
|
pattern++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*pattern == '\0')
|
|
|
|
return true; /* end of pattern, so declare match */
|
|
|
|
|
|
|
|
/* End of input string. Do we have matching pattern remaining? */
|
|
|
|
while (*pattern == '.' && pattern[1] == '*')
|
|
|
|
pattern += 2;
|
|
|
|
if (*pattern == '\0')
|
|
|
|
return true; /* end of pattern, so declare match */
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-01-19 17:42:24 +01:00
|
|
|
/*
|
2012-04-24 04:43:09 +02:00
|
|
|
* Replace all occurrences of a string in a string with a different string.
|
2007-01-19 17:42:24 +01:00
|
|
|
* NOTE: Assumes there is enough room in the target buffer!
|
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
void
|
2017-10-31 15:34:31 +01:00
|
|
|
replace_string(char *string, const char *replace, const char *replacement)
|
2007-01-19 17:42:24 +01:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *ptr;
|
2007-01-19 17:42:24 +01:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
while ((ptr = strstr(string, replace)) != NULL)
|
2007-01-19 17:42:24 +01:00
|
|
|
{
|
2016-08-31 00:22:43 +02:00
|
|
|
char *dup = pg_strdup(string);
|
2007-01-19 17:42:24 +01:00
|
|
|
|
2007-02-07 01:52:35 +01:00
|
|
|
strlcpy(string, dup, ptr - string + 1);
|
2007-01-19 17:42:24 +01:00
|
|
|
strcat(string, replacement);
|
|
|
|
strcat(string, dup + (ptr - string) + strlen(replace));
|
|
|
|
free(dup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert *.source found in the "source" directory, replacing certain tokens
|
|
|
|
* in the file contents with their intended values, and put the resulting files
|
|
|
|
* in the "dest" directory, replacing the ".source" prefix in their names with
|
|
|
|
* the given suffix.
|
|
|
|
*/
|
|
|
|
static void
|
2017-10-31 15:34:31 +01:00
|
|
|
convert_sourcefiles_in(const char *source_subdir, const char *dest_dir, const char *dest_subdir, const char *suffix)
|
2007-01-19 17:42:24 +01:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char testtablespace[MAXPGPATH];
|
|
|
|
char indir[MAXPGPATH];
|
2009-06-11 16:49:15 +02:00
|
|
|
struct stat st;
|
2007-11-27 20:13:30 +01:00
|
|
|
int ret;
|
2007-11-15 22:14:46 +01:00
|
|
|
char **name;
|
|
|
|
char **names;
|
|
|
|
int count = 0;
|
|
|
|
|
2008-10-02 00:38:57 +02:00
|
|
|
snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir);
|
2007-11-27 20:13:30 +01:00
|
|
|
|
|
|
|
/* Check that indir actually exists and is a directory */
|
|
|
|
ret = stat(indir, &st);
|
|
|
|
if (ret != 0 || !S_ISDIR(st.st_mode))
|
|
|
|
{
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* No warning, to avoid noise in tests that do not have these
|
|
|
|
* directories; for example, ecpg, contrib and src/pl.
|
2007-11-27 20:13:30 +01:00
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-01-19 17:42:24 +01:00
|
|
|
names = pgfnames(indir);
|
|
|
|
if (!names)
|
|
|
|
/* Error logged in pgfnames */
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2007-01-19 17:42:24 +01:00
|
|
|
|
2019-02-23 09:37:25 +01:00
|
|
|
snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
|
2008-01-19 18:43:42 +01:00
|
|
|
|
|
|
|
#ifdef WIN32
|
2014-05-06 18:12:18 +02:00
|
|
|
|
2008-01-19 18:43:42 +01:00
|
|
|
/*
|
|
|
|
* On Windows only, clean out the test tablespace dir, or create it if it
|
2009-06-11 16:49:15 +02:00
|
|
|
* 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.)
|
2008-01-19 18:43:42 +01:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2007-01-19 17:42:24 +01:00
|
|
|
if (directory_exists(testtablespace))
|
2014-03-02 04:14:14 +01:00
|
|
|
if (!rmtree(testtablespace, true))
|
|
|
|
{
|
2015-01-20 05:44:19 +01:00
|
|
|
fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\"\n"),
|
|
|
|
progname, testtablespace);
|
2014-03-02 04:14:14 +01:00
|
|
|
exit(2);
|
|
|
|
}
|
2007-01-19 17:42:24 +01:00
|
|
|
make_directory(testtablespace);
|
2008-01-19 18:43:42 +01:00
|
|
|
#endif
|
2007-01-19 17:42:24 +01:00
|
|
|
|
|
|
|
/* finally loop on each file and do the replacement */
|
|
|
|
for (name = names; *name; name++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char srcfile[MAXPGPATH];
|
|
|
|
char destfile[MAXPGPATH];
|
|
|
|
char prefix[MAXPGPATH];
|
|
|
|
FILE *infile,
|
|
|
|
*outfile;
|
|
|
|
char line[1024];
|
2007-01-19 17:42:24 +01:00
|
|
|
|
|
|
|
/* reject filenames not finishing in ".source" */
|
|
|
|
if (strlen(*name) < 8)
|
|
|
|
continue;
|
|
|
|
if (strcmp(*name + strlen(*name) - 7, ".source") != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
/* build the full actual paths to open */
|
|
|
|
snprintf(prefix, strlen(*name) - 6, "%s", *name);
|
|
|
|
snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name);
|
2012-06-10 21:20:04 +02:00
|
|
|
snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir,
|
2012-03-17 22:24:15 +01:00
|
|
|
prefix, suffix);
|
2007-01-19 17:42:24 +01:00
|
|
|
|
|
|
|
infile = fopen(srcfile, "r");
|
|
|
|
if (!infile)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, srcfile, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2007-01-19 17:42:24 +01:00
|
|
|
}
|
|
|
|
outfile = fopen(destfile, "w");
|
|
|
|
if (!outfile)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
|
2007-11-15 22:14:46 +01:00
|
|
|
progname, destfile, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2007-01-19 17:42:24 +01:00
|
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), infile))
|
|
|
|
{
|
2019-02-23 09:37:25 +01:00
|
|
|
replace_string(line, "@abs_srcdir@", inputdir);
|
|
|
|
replace_string(line, "@abs_builddir@", outputdir);
|
2007-01-19 17:42:24 +01:00
|
|
|
replace_string(line, "@testtablespace@", testtablespace);
|
2019-02-23 09:37:25 +01:00
|
|
|
replace_string(line, "@libdir@", dlpath);
|
2007-01-19 17:42:24 +01:00
|
|
|
replace_string(line, "@DLSUFFIX@", DLSUFFIX);
|
|
|
|
fputs(line, outfile);
|
|
|
|
}
|
|
|
|
fclose(infile);
|
|
|
|
fclose(outfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we didn't process any files, complain because it probably means
|
2008-10-02 00:38:57 +02:00
|
|
|
* somebody neglected to pass the needed --inputdir argument.
|
2007-01-19 17:42:24 +01:00
|
|
|
*/
|
|
|
|
if (count <= 0)
|
|
|
|
{
|
2008-08-05 07:16:08 +02:00
|
|
|
fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"),
|
2007-01-19 17:42:24 +01:00
|
|
|
progname, indir);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2007-01-19 17:42:24 +01:00
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
|
|
|
|
pgfnames_cleanup(names);
|
2007-01-19 17:42:24 +01:00
|
|
|
}
|
|
|
|
|
2007-01-19 22:21:13 +01:00
|
|
|
/* Create the .sql and .out files from the .source files, if any */
|
2007-01-19 17:42:24 +01:00
|
|
|
static void
|
|
|
|
convert_sourcefiles(void)
|
|
|
|
{
|
2015-02-15 03:33:41 +01:00
|
|
|
convert_sourcefiles_in("input", outputdir, "sql", "sql");
|
2012-03-17 22:24:15 +01:00
|
|
|
convert_sourcefiles_in("output", outputdir, "expected", "out");
|
2007-01-19 17:42:24 +01:00
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Scan resultmap file to find which platform-specific expected files to use.
|
|
|
|
*
|
|
|
|
* The format of each line of the file is
|
2006-10-04 02:30:14 +02:00
|
|
|
* testname/hostplatformpattern=substitutefile
|
2006-07-19 04:37:00 +02:00
|
|
|
* where the hostplatformpattern is evaluated per the rules of expr(1),
|
|
|
|
* namely, it is a standard regular expression with an implicit ^ at the start.
|
|
|
|
* (We currently support only a very limited subset of regular expressions,
|
|
|
|
* see string_matches_pattern() above.) What hostplatformpattern will be
|
2014-05-06 18:12:18 +02:00
|
|
|
* matched against is the config.guess output. (In the shell-script version,
|
2006-07-19 04:37:00 +02:00
|
|
|
* we also provided an indication of whether gcc or another compiler was in
|
|
|
|
* use, but that facility isn't used anymore.)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
load_resultmap(void)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char buf[MAXPGPATH];
|
|
|
|
FILE *f;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* scan the file ... */
|
|
|
|
snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
|
2006-10-04 02:30:14 +02:00
|
|
|
f = fopen(buf, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
/* OK if it doesn't exist, else complain */
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return;
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, buf, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2006-07-19 18:23:17 +02:00
|
|
|
|
|
|
|
while (fgets(buf, sizeof(buf), f))
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *platform;
|
2007-06-12 13:07:34 +02:00
|
|
|
char *file_type;
|
2006-10-04 02:30:14 +02:00
|
|
|
char *expected;
|
|
|
|
int i;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* strip trailing whitespace, especially the newline */
|
|
|
|
i = strlen(buf);
|
2006-10-04 02:30:14 +02:00
|
|
|
while (i > 0 && isspace((unsigned char) buf[i - 1]))
|
2006-07-19 04:37:00 +02:00
|
|
|
buf[--i] = '\0';
|
|
|
|
|
|
|
|
/* parse out the line fields */
|
2007-06-12 13:07:34 +02:00
|
|
|
file_type = strchr(buf, ':');
|
|
|
|
if (!file_type)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
|
2007-11-15 22:14:46 +01:00
|
|
|
buf);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2007-06-12 13:07:34 +02:00
|
|
|
}
|
|
|
|
*file_type++ = '\0';
|
|
|
|
|
|
|
|
platform = strchr(file_type, ':');
|
2006-07-19 04:37:00 +02:00
|
|
|
if (!platform)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
|
|
|
|
buf);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
*platform++ = '\0';
|
|
|
|
expected = strchr(platform, '=');
|
|
|
|
if (!expected)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
|
|
|
|
buf);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
*expected++ = '\0';
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* if it's for current platform, save it in resultmap list. Note: by
|
|
|
|
* adding at the front of the list, we ensure that in ambiguous cases,
|
|
|
|
* the last match in the resultmap file is used. This mimics the
|
|
|
|
* behavior of the old shell script.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
if (string_matches_pattern(host_platform, platform))
|
|
|
|
{
|
2016-08-31 00:22:43 +02:00
|
|
|
_resultmap *entry = pg_malloc(sizeof(_resultmap));
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2016-08-31 00:22:43 +02:00
|
|
|
entry->test = pg_strdup(buf);
|
|
|
|
entry->type = pg_strdup(file_type);
|
|
|
|
entry->resultfile = pg_strdup(expected);
|
2006-07-19 04:37:00 +02:00
|
|
|
entry->next = resultmap;
|
|
|
|
resultmap = entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Check in resultmap if we should be looking at a different file
|
|
|
|
*/
|
|
|
|
static
|
2007-11-15 22:14:46 +01:00
|
|
|
const char *
|
|
|
|
get_expectfile(const char *testname, const char *file)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *file_type;
|
2007-06-12 13:07:34 +02:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Handy subroutine for setting an environment variable "var" to "val"
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
doputenv(const char *var, const char *val)
|
|
|
|
{
|
2013-10-13 06:09:18 +02:00
|
|
|
char *s;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2013-10-23 01:40:26 +02:00
|
|
|
s = psprintf("%s=%s", var, val);
|
2006-07-19 04:37:00 +02:00
|
|
|
putenv(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare environment variables for running regression tests
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
initialize_environment(void)
|
|
|
|
{
|
2012-02-21 15:45:19 +01:00
|
|
|
putenv("PGAPPNAME=pg_regress");
|
|
|
|
|
2009-02-11 15:03:42 +01:00
|
|
|
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");
|
2015-01-16 07:27:31 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Most platforms have adopted the POSIX locale as their
|
|
|
|
* implementation-defined default locale. Exceptions include native
|
Refer to OS X as "macOS", except for the port name which is still "darwin".
We weren't terribly consistent about whether to call Apple's OS "OS X"
or "Mac OS X", and the former is probably confusing to people who aren't
Apple users. Now that Apple has rebranded it "macOS", follow their lead
to establish a consistent naming pattern. Also, avoid the use of the
ancient project name "Darwin", except as the port code name which does not
seem desirable to change. (In short, this patch touches documentation and
comments, but no actual code.)
I didn't touch contrib/start-scripts/osx/, either. I suspect those are
obsolete and due for a rewrite, anyway.
I dithered about whether to apply this edit to old release notes, but
those were responsible for quite a lot of the inconsistencies, so I ended
up changing them too. Anyway, Apple's being ahistorical about this,
so why shouldn't we be?
2016-09-25 21:40:57 +02:00
|
|
|
* Windows, macOS with --enable-nls, and Cygwin with --enable-nls.
|
2015-01-16 07:27:31 +01:00
|
|
|
* (Use of --enable-nls matters because libintl replaces setlocale().)
|
Refer to OS X as "macOS", except for the port name which is still "darwin".
We weren't terribly consistent about whether to call Apple's OS "OS X"
or "Mac OS X", and the former is probably confusing to people who aren't
Apple users. Now that Apple has rebranded it "macOS", follow their lead
to establish a consistent naming pattern. Also, avoid the use of the
ancient project name "Darwin", except as the port code name which does not
seem desirable to change. (In short, this patch touches documentation and
comments, but no actual code.)
I didn't touch contrib/start-scripts/osx/, either. I suspect those are
obsolete and due for a rewrite, anyway.
I dithered about whether to apply this edit to old release notes, but
those were responsible for quite a lot of the inconsistencies, so I ended
up changing them too. Anyway, Apple's being ahistorical about this,
so why shouldn't we be?
2016-09-25 21:40:57 +02:00
|
|
|
* Also, PostgreSQL does not support macOS with locale environment
|
2015-01-16 07:27:31 +01:00
|
|
|
* variables unset; see PostmasterMain().
|
|
|
|
*/
|
|
|
|
#if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
|
|
|
|
putenv("LANG=C");
|
2006-07-19 04:37:00 +02:00
|
|
|
#endif
|
2009-02-11 15:03:42 +01:00
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2009-02-12 14:26:03 +01:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* 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.)
|
2009-02-12 14:26:03 +01:00
|
|
|
*/
|
|
|
|
unsetenv("LANGUAGE");
|
|
|
|
unsetenv("LC_ALL");
|
|
|
|
putenv("LC_MESSAGES=C");
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
2011-04-15 07:42:05 +02:00
|
|
|
* Set encoding as requested
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
if (encoding)
|
|
|
|
doputenv("PGCLIENTENCODING", encoding);
|
|
|
|
else
|
|
|
|
unsetenv("PGCLIENTENCODING");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set timezone and datestyle for datetime-related tests
|
|
|
|
*/
|
|
|
|
putenv("PGTZ=PST8PDT");
|
|
|
|
putenv("PGDATESTYLE=Postgres, MDY");
|
2008-11-25 20:30:42 +01:00
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* 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.
|
2008-11-25 20:30:42 +01:00
|
|
|
*/
|
|
|
|
{
|
2008-11-26 14:26:52 +01:00
|
|
|
const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
|
2008-11-25 20:30:42 +01:00
|
|
|
const char *old_pgoptions = getenv("PGOPTIONS");
|
2009-06-11 16:49:15 +02:00
|
|
|
char *new_pgoptions;
|
2008-11-25 20:30:42 +01:00
|
|
|
|
|
|
|
if (!old_pgoptions)
|
|
|
|
old_pgoptions = "";
|
2013-10-23 01:40:26 +02:00
|
|
|
new_pgoptions = psprintf("PGOPTIONS=%s %s",
|
|
|
|
old_pgoptions, my_pgoptions);
|
2008-11-25 20:30:42 +01:00
|
|
|
putenv(new_pgoptions);
|
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2015-04-23 14:59:52 +02:00
|
|
|
if (temp_instance)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Clear out any environment vars that might cause psql to connect to
|
|
|
|
* the wrong postmaster, or otherwise behave in nondefault ways. (Note
|
|
|
|
* we also use psql's -X switch consistently, so that ~/.psqlrc files
|
|
|
|
* won't mess things up.) Also, set PGPORT to the temp port, and set
|
2014-06-14 15:41:13 +02:00
|
|
|
* PGHOST depending on whether we are using TCP or Unix sockets.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
unsetenv("PGDATABASE");
|
|
|
|
unsetenv("PGUSER");
|
|
|
|
unsetenv("PGSERVICE");
|
|
|
|
unsetenv("PGSSLMODE");
|
|
|
|
unsetenv("PGREQUIRESSL");
|
|
|
|
unsetenv("PGCONNECT_TIMEOUT");
|
|
|
|
unsetenv("PGDATA");
|
2014-06-14 15:41:13 +02:00
|
|
|
#ifdef HAVE_UNIX_SOCKETS
|
2006-07-19 04:37:00 +02:00
|
|
|
if (hostname != NULL)
|
|
|
|
doputenv("PGHOST", hostname);
|
|
|
|
else
|
2014-06-14 15:41:13 +02:00
|
|
|
{
|
|
|
|
sockdir = getenv("PG_REGRESS_SOCK_DIR");
|
|
|
|
if (!sockdir)
|
|
|
|
sockdir = make_temp_sockdir();
|
|
|
|
doputenv("PGHOST", sockdir);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
Assert(hostname != NULL);
|
|
|
|
doputenv("PGHOST", hostname);
|
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
unsetenv("PGHOSTADDR");
|
|
|
|
if (port != -1)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char s[16];
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
sprintf(s, "%d", port);
|
|
|
|
doputenv("PGPORT", s);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *pghost;
|
|
|
|
const char *pgport;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When testing an existing install, we honor existing environment
|
|
|
|
* variables, except if they're overridden by command line options.
|
|
|
|
*/
|
|
|
|
if (hostname != NULL)
|
|
|
|
{
|
|
|
|
doputenv("PGHOST", hostname);
|
|
|
|
unsetenv("PGHOSTADDR");
|
|
|
|
}
|
|
|
|
if (port != -1)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char s[16];
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
sprintf(s, "%d", port);
|
|
|
|
doputenv("PGPORT", s);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
if (user != NULL)
|
|
|
|
doputenv("PGUSER", user);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report what we're connecting to
|
|
|
|
*/
|
|
|
|
pghost = getenv("PGHOST");
|
|
|
|
pgport = getenv("PGPORT");
|
|
|
|
#ifndef HAVE_UNIX_SOCKETS
|
|
|
|
if (!pghost)
|
|
|
|
pghost = "localhost";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pghost && pgport)
|
|
|
|
printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport);
|
|
|
|
if (pghost && !pgport)
|
|
|
|
printf(_("(using postmaster on %s, default port)\n"), pghost);
|
|
|
|
if (!pghost && pgport)
|
|
|
|
printf(_("(using postmaster on Unix socket, port %s)\n"), pgport);
|
|
|
|
if (!pghost && !pgport)
|
|
|
|
printf(_("(using postmaster on Unix socket, default port)\n"));
|
|
|
|
}
|
|
|
|
|
2007-01-19 17:42:24 +01:00
|
|
|
convert_sourcefiles();
|
2006-07-19 04:37:00 +02:00
|
|
|
load_resultmap();
|
|
|
|
}
|
|
|
|
|
2019-04-28 18:45:55 +02:00
|
|
|
#ifdef ENABLE_SSPI
|
|
|
|
|
|
|
|
/* support for config_sspi_auth() */
|
2016-08-04 20:44:23 +02:00
|
|
|
static const char *
|
|
|
|
fmtHba(const char *raw)
|
|
|
|
{
|
|
|
|
static char *ret;
|
|
|
|
const char *rp;
|
|
|
|
char *wp;
|
|
|
|
|
|
|
|
wp = ret = realloc(ret, 3 + strlen(raw) * 2);
|
|
|
|
|
|
|
|
*wp++ = '"';
|
|
|
|
for (rp = raw; *rp; rp++)
|
|
|
|
{
|
|
|
|
if (*rp == '"')
|
|
|
|
*wp++ = '"';
|
|
|
|
*wp++ = *rp;
|
|
|
|
}
|
|
|
|
*wp++ = '"';
|
|
|
|
*wp++ = '\0';
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-12-18 04:48:40 +01:00
|
|
|
/*
|
|
|
|
* Get account and domain/realm names for the current user. This is based on
|
|
|
|
* pg_SSPI_recvauth(). The returned strings use static storage.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
current_windows_user(const char **acct, const char **dom)
|
|
|
|
{
|
|
|
|
static char accountname[MAXPGPATH];
|
|
|
|
static char domainname[MAXPGPATH];
|
|
|
|
HANDLE token;
|
|
|
|
TOKEN_USER *tokenuser;
|
|
|
|
DWORD retlen;
|
|
|
|
DWORD accountnamesize = sizeof(accountname);
|
|
|
|
DWORD domainnamesize = sizeof(domainname);
|
|
|
|
SID_NAME_USE accountnameuse;
|
|
|
|
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
_("%s: could not open process token: error code %lu\n"),
|
|
|
|
progname, GetLastError());
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
2016-04-07 05:41:43 +02:00
|
|
|
_("%s: could not get token information buffer size: error code %lu\n"),
|
2014-12-18 04:48:40 +01:00
|
|
|
progname, GetLastError());
|
|
|
|
exit(2);
|
|
|
|
}
|
2016-08-31 00:22:43 +02:00
|
|
|
tokenuser = pg_malloc(retlen);
|
2014-12-18 04:48:40 +01:00
|
|
|
if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
2016-04-07 05:41:43 +02:00
|
|
|
_("%s: could not get token information: error code %lu\n"),
|
2014-12-18 04:48:40 +01:00
|
|
|
progname, GetLastError());
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
|
|
|
|
domainname, &domainnamesize, &accountnameuse))
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
_("%s: could not look up account SID: error code %lu\n"),
|
|
|
|
progname, GetLastError());
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(tokenuser);
|
|
|
|
|
|
|
|
*acct = accountname;
|
|
|
|
*dom = domainname;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
|
|
|
|
* the current OS user to authenticate as the bootstrap superuser and as any
|
|
|
|
* user named in a --create-role option.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
config_sspi_auth(const char *pgdata)
|
|
|
|
{
|
|
|
|
const char *accountname,
|
|
|
|
*domainname;
|
|
|
|
const char *username;
|
|
|
|
char *errstr;
|
2014-12-25 19:52:03 +01:00
|
|
|
bool have_ipv6;
|
2014-12-18 04:48:40 +01:00
|
|
|
char fname[MAXPGPATH];
|
|
|
|
int res;
|
|
|
|
FILE *hba,
|
|
|
|
*ident;
|
|
|
|
_stringlist *sl;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "username", the initdb-chosen bootstrap superuser name, may always
|
|
|
|
* match "accountname", the value SSPI authentication discovers. The
|
|
|
|
* underlying system functions do not clearly guarantee that.
|
|
|
|
*/
|
|
|
|
current_windows_user(&accountname, &domainname);
|
|
|
|
username = get_user_name(&errstr);
|
|
|
|
if (username == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: %s\n", progname, errstr);
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
2014-12-25 19:52:03 +01:00
|
|
|
/*
|
|
|
|
* Like initdb.c:setup_config(), determine whether the platform recognizes
|
|
|
|
* ::1 (IPv6 loopback) as a numeric host address string.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct addrinfo *gai_result;
|
|
|
|
struct addrinfo hints;
|
|
|
|
WSADATA wsaData;
|
|
|
|
|
|
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_socktype = 0;
|
|
|
|
hints.ai_protocol = 0;
|
|
|
|
hints.ai_addrlen = 0;
|
|
|
|
hints.ai_canonname = NULL;
|
|
|
|
hints.ai_addr = NULL;
|
|
|
|
hints.ai_next = NULL;
|
|
|
|
|
|
|
|
have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 &&
|
|
|
|
getaddrinfo("::1", NULL, &hints, &gai_result) == 0);
|
|
|
|
}
|
|
|
|
|
2014-12-18 04:48:40 +01:00
|
|
|
/* Check a Write outcome and report any error. */
|
|
|
|
#define CW(cond) \
|
|
|
|
do { \
|
|
|
|
if (!(cond)) \
|
|
|
|
{ \
|
|
|
|
fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \
|
|
|
|
progname, fname, strerror(errno)); \
|
|
|
|
exit(2); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
|
Clean up assorted misuses of snprintf()'s result value.
Fix a small number of places that were testing the result of snprintf()
but doing so incorrectly. The right test for buffer overrun, per C99,
is "result >= bufsize" not "result > bufsize". Some places were also
checking for failure with "result == -1", but the standard only says
that a negative value is delivered on failure.
(Note that this only makes these places correct if snprintf() delivers
C99-compliant results. But at least now these places are consistent
with all the other places where we assume that.)
Also, make psql_start_test() and isolation_start_test() check for
buffer overrun while constructing their shell commands. There seems
like a higher risk of overrun, with more severe consequences, here
than there is for the individual file paths that are made elsewhere
in the same functions, so this seemed like a worthwhile change.
Also fix guc.c's do_serialize() to initialize errno = 0 before
calling vsnprintf. In principle, this should be unnecessary because
vsnprintf should have set errno if it returns a failure indication ...
but the other two places this coding pattern is cribbed from don't
assume that, so let's be consistent.
These errors are all very old, so back-patch as appropriate. I think
that only the shell command overrun cases are even theoretically
reachable in practice, but there's not much point in erroneous error
checks.
Discussion: https://postgr.es/m/17245.1534289329@sss.pgh.pa.us
2018-08-15 22:29:31 +02:00
|
|
|
if (res < 0 || res >= sizeof(fname))
|
2014-12-18 04:48:40 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Truncating this name is a fatal error, because we must not fail to
|
|
|
|
* overwrite an original trust-authentication pg_hba.conf.
|
|
|
|
*/
|
|
|
|
fprintf(stderr, _("%s: directory name too long\n"), progname);
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
hba = fopen(fname, "w");
|
|
|
|
if (hba == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
|
|
|
|
progname, fname, strerror(errno));
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
|
|
|
|
CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
|
|
|
|
hba) >= 0);
|
2014-12-25 19:52:03 +01:00
|
|
|
if (have_ipv6)
|
|
|
|
CW(fputs("host all all ::1/128 sspi include_realm=1 map=regress\n",
|
|
|
|
hba) >= 0);
|
2014-12-18 04:48:40 +01:00
|
|
|
CW(fclose(hba) == 0);
|
|
|
|
|
|
|
|
snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
|
|
|
|
ident = fopen(fname, "w");
|
|
|
|
if (ident == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
|
|
|
|
progname, fname, strerror(errno));
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Double-quote for the benefit of account names containing whitespace or
|
|
|
|
* '#'. Windows forbids the double-quote character itself, so don't
|
|
|
|
* bother escaping embedded double-quote characters.
|
|
|
|
*/
|
2016-08-04 20:44:23 +02:00
|
|
|
CW(fprintf(ident, "regress \"%s@%s\" %s\n",
|
|
|
|
accountname, domainname, fmtHba(username)) >= 0);
|
2014-12-18 04:48:40 +01:00
|
|
|
for (sl = extraroles; sl; sl = sl->next)
|
2016-08-04 20:44:23 +02:00
|
|
|
CW(fprintf(ident, "regress \"%s@%s\" %s\n",
|
|
|
|
accountname, domainname, fmtHba(sl->str)) >= 0);
|
2014-12-18 04:48:40 +01:00
|
|
|
CW(fclose(ident) == 0);
|
|
|
|
}
|
2019-04-28 18:45:55 +02:00
|
|
|
|
|
|
|
#endif /* ENABLE_SSPI */
|
2014-12-18 04:48:40 +01:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Issue a command via psql, connecting to the specified database
|
|
|
|
*
|
|
|
|
* Since we use system(), this doesn't return until the operation finishes
|
|
|
|
*/
|
|
|
|
static void
|
2006-10-04 02:30:14 +02:00
|
|
|
psql_command(const char *database, const char *query,...)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char query_formatted[1024];
|
|
|
|
char query_escaped[2048];
|
|
|
|
char psql_cmd[MAXPGPATH + 2048];
|
|
|
|
va_list args;
|
|
|
|
char *s;
|
|
|
|
char *d;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* Generate the query with insertion of sprintf arguments */
|
|
|
|
va_start(args, query);
|
|
|
|
vsnprintf(query_formatted, sizeof(query_formatted), query, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
/* Now escape any shell double-quote metacharacters */
|
|
|
|
d = query_escaped;
|
|
|
|
for (s = query_formatted; *s; s++)
|
|
|
|
{
|
|
|
|
if (strchr("\\\"$`", *s))
|
|
|
|
*d++ = '\\';
|
|
|
|
*d++ = *s;
|
|
|
|
}
|
|
|
|
*d = '\0';
|
|
|
|
|
|
|
|
/* And now we can build and execute the shell command */
|
|
|
|
snprintf(psql_cmd, sizeof(psql_cmd),
|
Replace SYSTEMQUOTEs with Windows-specific wrapper functions.
It's easy to forget using SYSTEMQUOTEs when constructing command strings
for system() or popen(). Even if we fix all the places missing it now, it is
bound to be forgotten again in the future. Introduce wrapper functions that
do the the extra quoting for you, and get rid of SYSTEMQUOTEs in all the
callers.
We previosly used SYSTEMQUOTEs in all the hard-coded command strings, and
this doesn't change the behavior of those. But user-supplied commands, like
archive_command, restore_command, COPY TO/FROM PROGRAM calls, as well as
pgbench's \shell, will now gain an extra pair of quotes. That is desirable,
but if you have existing scripts or config files that include an extra
pair of quotes, those might need to be adjusted.
Reviewed by Amit Kapila and Tom Lane
2014-05-05 15:07:40 +02:00
|
|
|
"\"%s%spsql\" -X -c \"%s\" \"%s\"",
|
2015-04-23 14:59:52 +02:00
|
|
|
bindir ? bindir : "",
|
|
|
|
bindir ? "/" : "",
|
2006-07-21 02:24:04 +02:00
|
|
|
query_escaped,
|
|
|
|
database);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (system(psql_cmd) != 0)
|
|
|
|
{
|
|
|
|
/* psql probably already reported the error */
|
|
|
|
fprintf(stderr, _("command failed: %s\n"), psql_cmd);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Spawn a process to execute the given shell command; don't wait for it
|
|
|
|
*
|
2006-09-24 19:10:18 +02:00
|
|
|
* Returns the process ID (or HANDLE) so we can wait for it later
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
2007-06-12 13:07:34 +02:00
|
|
|
PID_TYPE
|
2006-07-19 04:37:00 +02:00
|
|
|
spawn_process(const char *cmdline)
|
|
|
|
{
|
|
|
|
#ifndef WIN32
|
2006-10-04 02:30:14 +02:00
|
|
|
pid_t pid;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here
|
2006-07-19 04:37:00 +02:00
|
|
|
* ... does anyone still care about systems where that doesn't work?
|
|
|
|
*/
|
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
if (logfile)
|
|
|
|
fflush(logfile);
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not fork: %s\n"),
|
|
|
|
progname, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
if (pid == 0)
|
|
|
|
{
|
2006-07-19 19:02:59 +02:00
|
|
|
/*
|
|
|
|
* In child
|
|
|
|
*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Instead of using system(), exec the shell directly, and tell it to
|
2014-05-06 18:12:18 +02:00
|
|
|
* "exec" the command too. This saves two useless processes per
|
2006-10-04 02:30:14 +02:00
|
|
|
* parallel test case.
|
2006-07-19 19:02:59 +02:00
|
|
|
*/
|
2013-10-13 06:09:18 +02:00
|
|
|
char *cmdline2;
|
2006-07-19 19:02:59 +02:00
|
|
|
|
2013-10-23 01:40:26 +02:00
|
|
|
cmdline2 = psprintf("exec %s", cmdline);
|
2007-07-18 23:19:17 +02:00
|
|
|
execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
|
2006-07-19 19:02:59 +02:00
|
|
|
fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
|
|
|
|
progname, shellprog, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
_exit(1); /* not exit() here... */
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
/* in parent */
|
|
|
|
return pid;
|
|
|
|
#else
|
2015-05-24 03:35:49 +02:00
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
char *cmdline2;
|
|
|
|
HANDLE restrictedToken;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2015-03-30 23:07:52 +02:00
|
|
|
memset(&pi, 0, sizeof(pi));
|
Replace SYSTEMQUOTEs with Windows-specific wrapper functions.
It's easy to forget using SYSTEMQUOTEs when constructing command strings
for system() or popen(). Even if we fix all the places missing it now, it is
bound to be forgotten again in the future. Introduce wrapper functions that
do the the extra quoting for you, and get rid of SYSTEMQUOTEs in all the
callers.
We previosly used SYSTEMQUOTEs in all the hard-coded command strings, and
this doesn't change the behavior of those. But user-supplied commands, like
archive_command, restore_command, COPY TO/FROM PROGRAM calls, as well as
pgbench's \shell, will now gain an extra pair of quotes. That is desirable,
but if you have existing scripts or config files that include an extra
pair of quotes, those might need to be adjusted.
Reviewed by Amit Kapila and Tom Lane
2014-05-05 15:07:40 +02:00
|
|
|
cmdline2 = psprintf("cmd /c \"%s\"", cmdline);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2015-05-24 03:35:49 +02:00
|
|
|
if ((restrictedToken =
|
Unified logging system for command-line programs
This unifies the various ad hoc logging (message printing, error
printing) systems used throughout the command-line programs.
Features:
- Program name is automatically prefixed.
- Message string does not end with newline. This removes a common
source of inconsistencies and omissions.
- Additionally, a final newline is automatically stripped, simplifying
use of PQerrorMessage() etc., another common source of mistakes.
- I converted error message strings to use %m where possible.
- As a result of the above several points, more translatable message
strings can be shared between different components and between
frontends and backend, without gratuitous punctuation or whitespace
differences.
- There is support for setting a "log level". This is not meant to be
user-facing, but can be used internally to implement debug or
verbose modes.
- Lazy argument evaluation, so no significant overhead if logging at
some level is disabled.
- Some color in the messages, similar to gcc and clang. Set
PG_COLOR=auto to try it out. Some colors are predefined, but can be
customized by setting PG_COLORS.
- Common files (common/, fe_utils/, etc.) can handle logging much more
simply by just using one API without worrying too much about the
context of the calling program, requiring callbacks, or having to
pass "progname" around everywhere.
- Some programs called setvbuf() to make sure that stderr is
unbuffered, even on Windows. But not all programs did that. This
is now done centrally.
Soft goals:
- Reduces vertical space use and visual complexity of error reporting
in the source code.
- Encourages more deliberate classification of messages. For example,
in some cases it wasn't clear without analyzing the surrounding code
whether a message was meant as an error or just an info.
- Concepts and terms are vaguely aligned with popular logging
frameworks such as log4j and Python logging.
This is all just about printing stuff out. Nothing affects program
flow (e.g., fatal exits). The uses are just too varied to do that.
Some existing code had wrappers that do some kind of print-and-exit,
and I adapted those.
I tried to keep the output mostly the same, but there is a lot of
historical baggage to unwind and special cases to consider, and I
might not always have succeeded. One significant change is that
pg_rewind used to write all error messages to stdout. That is now
changed to stderr.
Reviewed-by: Donald Dong <xdong@csumb.edu>
Reviewed-by: Arthur Zakirov <a.zakirov@postgrespro.ru>
Discussion: https://www.postgresql.org/message-id/flat/6a609b43-4f57-7348-6480-bd022f924310@2ndquadrant.com
2019-04-01 14:24:37 +02:00
|
|
|
CreateRestrictedProcess(cmdline2, &pi)) == 0)
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
return pi.hProcess;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Count bytes in file
|
|
|
|
*/
|
|
|
|
static long
|
|
|
|
file_size(const char *file)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
long r;
|
|
|
|
FILE *f = fopen(file, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, file, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
|
|
r = ftell(f);
|
|
|
|
fclose(f);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Count lines in file
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
file_line_count(const char *file)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int c;
|
|
|
|
int l = 0;
|
|
|
|
FILE *f = fopen(file, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, file, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
while ((c = fgetc(f)) != EOF)
|
|
|
|
{
|
|
|
|
if (c == '\n')
|
|
|
|
l++;
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2008-10-02 00:38:57 +02:00
|
|
|
bool
|
2006-07-19 04:37:00 +02:00
|
|
|
file_exists(const char *file)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
FILE *f = fopen(file, "r");
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return false;
|
|
|
|
fclose(f);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
directory_exists(const char *dir)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (stat(dir, &st) != 0)
|
|
|
|
return false;
|
Fix a number of places that were making file-type tests infelicitously.
The places that did, eg,
(statbuf.st_mode & S_IFMT) == S_IFDIR
were correct, but there is no good reason not to use S_ISDIR() instead,
especially when that's what the other 90% of our code does. The places
that did, eg,
(statbuf.st_mode & S_IFDIR)
were flat out *wrong* and would fail in various platform-specific ways,
eg a symlink could be mistaken for a regular file on most Unixen.
The actual impact of this is probably small, since the problem cases
seem to always involve symlinks or sockets, which are unlikely to be
found in the directories that PG code might be scanning. But it's
clearly trouble waiting to happen, so patch all the way back anyway.
(There seem to be no occurrences of the mistake in 7.4.)
2008-03-31 03:31:43 +02:00
|
|
|
if (S_ISDIR(st.st_mode))
|
2006-07-19 04:37:00 +02:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a directory */
|
|
|
|
static void
|
|
|
|
make_directory(const char *dir)
|
|
|
|
{
|
2006-07-27 17:37:19 +02:00
|
|
|
if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
|
|
|
|
progname, dir, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
get_alternative_expectfile(const char *expectfile, int i)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *last_dot;
|
|
|
|
int ssize = strlen(expectfile) + 2 + 1;
|
2014-03-03 09:18:51 +01:00
|
|
|
char *tmp;
|
|
|
|
char *s;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2014-05-06 18:12:18 +02:00
|
|
|
if (!(tmp = (char *) malloc(ssize)))
|
2014-03-02 04:14:14 +01:00
|
|
|
return NULL;
|
2014-03-03 20:05:33 +01:00
|
|
|
|
2014-05-06 18:12:18 +02:00
|
|
|
if (!(s = (char *) malloc(ssize)))
|
2014-03-03 09:18:51 +01:00
|
|
|
{
|
|
|
|
free(tmp);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-03-02 04:14:14 +01:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
strcpy(tmp, expectfile);
|
2007-11-15 22:14:46 +01:00
|
|
|
last_dot = strrchr(tmp, '.');
|
2007-06-12 13:07:34 +02:00
|
|
|
if (!last_dot)
|
2009-01-08 21:09:06 +01:00
|
|
|
{
|
|
|
|
free(tmp);
|
|
|
|
free(s);
|
2007-06-12 13:07:34 +02:00
|
|
|
return NULL;
|
2009-01-08 21:09:06 +01:00
|
|
|
}
|
2007-06-12 13:07:34 +02:00
|
|
|
*last_dot = '\0';
|
2007-11-15 22:14:46 +01:00
|
|
|
snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
|
2007-06-12 13:07:34 +02:00
|
|
|
free(tmp);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:25:30 +02:00
|
|
|
/*
|
2006-07-30 03:45:21 +02:00
|
|
|
* Run a "diff" command and also check that it didn't crash
|
2006-07-20 18:25:30 +02:00
|
|
|
*/
|
2006-07-30 03:45:21 +02:00
|
|
|
static int
|
|
|
|
run_diff(const char *cmd, const char *filename)
|
2006-07-20 18:25:30 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int r;
|
2006-07-20 18:25:30 +02:00
|
|
|
|
|
|
|
r = system(cmd);
|
|
|
|
if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-20 18:25:30 +02:00
|
|
|
}
|
2006-07-30 03:45:21 +02:00
|
|
|
#ifdef WIN32
|
2014-05-06 18:12:18 +02:00
|
|
|
|
2006-07-30 03:45:21 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* On WIN32, if the 'diff' command cannot be found, system() returns 1,
|
|
|
|
* but produces nothing to stdout, so we check for that here.
|
2006-07-30 03:45:21 +02:00
|
|
|
*/
|
|
|
|
if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("diff command not found: %s\n"), cmd);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-30 03:45:21 +02:00
|
|
|
}
|
|
|
|
#endif
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-07-30 03:45:21 +02:00
|
|
|
return WEXITSTATUS(r);
|
2006-07-20 18:25:30 +02:00
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Check the actual result file for the given test against expected results
|
|
|
|
*
|
|
|
|
* Returns true if different (failure), false if correct match found.
|
|
|
|
* In the true case, the diff is appended to the diffs file.
|
|
|
|
*/
|
|
|
|
static bool
|
2007-06-12 13:07:34 +02:00
|
|
|
results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char expectfile[MAXPGPATH];
|
|
|
|
char diff[MAXPGPATH];
|
|
|
|
char cmd[MAXPGPATH * 3];
|
|
|
|
char best_expect_file[MAXPGPATH];
|
|
|
|
FILE *difffile;
|
|
|
|
int best_line_count;
|
|
|
|
int i;
|
|
|
|
int l;
|
2007-06-12 13:07:34 +02:00
|
|
|
const char *platform_expectfile;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* We can pass either the resultsfile or the expectfile, they should have
|
|
|
|
* the same type (filename.type) anyway.
|
2007-06-12 13:07:34 +02:00
|
|
|
*/
|
|
|
|
platform_expectfile = get_expectfile(testname, resultsfile);
|
|
|
|
|
2014-02-17 17:20:21 +01:00
|
|
|
strlcpy(expectfile, default_expectfile, sizeof(expectfile));
|
2007-11-15 22:14:46 +01:00
|
|
|
if (platform_expectfile)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
2016-04-02 17:12:17 +02:00
|
|
|
* Replace everything after the last slash in expectfile with what the
|
2007-06-12 13:07:34 +02:00
|
|
|
* platform_expectfile contains.
|
|
|
|
*/
|
2007-11-15 22:14:46 +01:00
|
|
|
char *p = strrchr(expectfile, '/');
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
if (p)
|
|
|
|
strcpy(++p, platform_expectfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Name to use for temporary diff file */
|
2007-06-12 13:07:34 +02:00
|
|
|
snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* OK, run the diff */
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
Replace SYSTEMQUOTEs with Windows-specific wrapper functions.
It's easy to forget using SYSTEMQUOTEs when constructing command strings
for system() or popen(). Even if we fix all the places missing it now, it is
bound to be forgotten again in the future. Introduce wrapper functions that
do the the extra quoting for you, and get rid of SYSTEMQUOTEs in all the
callers.
We previosly used SYSTEMQUOTEs in all the hard-coded command strings, and
this doesn't change the behavior of those. But user-supplied commands, like
archive_command, restore_command, COPY TO/FROM PROGRAM calls, as well as
pgbench's \shell, will now gain an extra pair of quotes. That is desirable,
but if you have existing scripts or config files that include an extra
pair of quotes, those might need to be adjusted.
Reviewed by Amit Kapila and Tom Lane
2014-05-05 15:07:40 +02:00
|
|
|
"diff %s \"%s\" \"%s\" > \"%s\"",
|
2006-07-19 04:37:00 +02:00
|
|
|
basic_diff_opts, expectfile, resultsfile, diff);
|
|
|
|
|
|
|
|
/* Is the diff file empty? */
|
2006-07-30 03:45:21 +02:00
|
|
|
if (run_diff(cmd, diff) == 0)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
unlink(diff);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* There may be secondary comparison files that match better */
|
|
|
|
best_line_count = file_line_count(diff);
|
|
|
|
strcpy(best_expect_file, expectfile);
|
|
|
|
|
|
|
|
for (i = 0; i <= 9; i++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
char *alt_expectfile;
|
2007-06-12 13:07:34 +02:00
|
|
|
|
|
|
|
alt_expectfile = get_alternative_expectfile(expectfile, i);
|
2014-03-02 04:14:14 +01:00
|
|
|
if (!alt_expectfile)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("Unable to check secondary comparison files: %s\n"),
|
|
|
|
strerror(errno));
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
if (!file_exists(alt_expectfile))
|
2014-03-02 04:14:14 +01:00
|
|
|
{
|
|
|
|
free(alt_expectfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
continue;
|
2014-03-02 04:14:14 +01:00
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
Replace SYSTEMQUOTEs with Windows-specific wrapper functions.
It's easy to forget using SYSTEMQUOTEs when constructing command strings
for system() or popen(). Even if we fix all the places missing it now, it is
bound to be forgotten again in the future. Introduce wrapper functions that
do the the extra quoting for you, and get rid of SYSTEMQUOTEs in all the
callers.
We previosly used SYSTEMQUOTEs in all the hard-coded command strings, and
this doesn't change the behavior of those. But user-supplied commands, like
archive_command, restore_command, COPY TO/FROM PROGRAM calls, as well as
pgbench's \shell, will now gain an extra pair of quotes. That is desirable,
but if you have existing scripts or config files that include an extra
pair of quotes, those might need to be adjusted.
Reviewed by Amit Kapila and Tom Lane
2014-05-05 15:07:40 +02:00
|
|
|
"diff %s \"%s\" \"%s\" > \"%s\"",
|
2007-06-12 13:07:34 +02:00
|
|
|
basic_diff_opts, alt_expectfile, resultsfile, diff);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-30 03:45:21 +02:00
|
|
|
if (run_diff(cmd, diff) == 0)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
unlink(diff);
|
2014-03-02 04:14:14 +01:00
|
|
|
free(alt_expectfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
l = file_line_count(diff);
|
|
|
|
if (l < best_line_count)
|
|
|
|
{
|
|
|
|
/* This diff was a better match than the last one */
|
|
|
|
best_line_count = l;
|
2014-02-17 17:20:21 +01:00
|
|
|
strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file));
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2007-06-12 13:07:34 +02:00
|
|
|
free(alt_expectfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
/*
|
|
|
|
* fall back on the canonical results file if we haven't tried it yet and
|
|
|
|
* haven't found a complete match yet.
|
2006-08-01 16:56:29 +02:00
|
|
|
*/
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
if (platform_expectfile)
|
2006-08-01 16:56:29 +02:00
|
|
|
{
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
Replace SYSTEMQUOTEs with Windows-specific wrapper functions.
It's easy to forget using SYSTEMQUOTEs when constructing command strings
for system() or popen(). Even if we fix all the places missing it now, it is
bound to be forgotten again in the future. Introduce wrapper functions that
do the the extra quoting for you, and get rid of SYSTEMQUOTEs in all the
callers.
We previosly used SYSTEMQUOTEs in all the hard-coded command strings, and
this doesn't change the behavior of those. But user-supplied commands, like
archive_command, restore_command, COPY TO/FROM PROGRAM calls, as well as
pgbench's \shell, will now gain an extra pair of quotes. That is desirable,
but if you have existing scripts or config files that include an extra
pair of quotes, those might need to be adjusted.
Reviewed by Amit Kapila and Tom Lane
2014-05-05 15:07:40 +02:00
|
|
|
"diff %s \"%s\" \"%s\" > \"%s\"",
|
2007-06-12 13:07:34 +02:00
|
|
|
basic_diff_opts, default_expectfile, resultsfile, diff);
|
2006-08-01 16:56:29 +02:00
|
|
|
|
|
|
|
if (run_diff(cmd, diff) == 0)
|
|
|
|
{
|
|
|
|
/* No diff = no changes = good */
|
|
|
|
unlink(diff);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
l = file_line_count(diff);
|
|
|
|
if (l < best_line_count)
|
|
|
|
{
|
|
|
|
/* This diff was a better match than the last one */
|
|
|
|
best_line_count = l;
|
2014-02-17 17:20:21 +01:00
|
|
|
strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file));
|
2006-08-01 16:56:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Use the best comparison file to generate the "pretty" diff, which we
|
|
|
|
* append to the diffs summary file.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
|
2019-01-02 21:24:51 +01:00
|
|
|
/* Write diff header */
|
2006-07-19 04:37:00 +02:00
|
|
|
difffile = fopen(difffilename, "a");
|
|
|
|
if (difffile)
|
|
|
|
{
|
|
|
|
fprintf(difffile,
|
2019-01-02 21:24:51 +01:00
|
|
|
"diff %s %s %s\n",
|
|
|
|
pretty_diff_opts, best_expect_file, resultsfile);
|
2006-07-19 04:37:00 +02:00
|
|
|
fclose(difffile);
|
|
|
|
}
|
|
|
|
|
2019-01-02 21:24:51 +01:00
|
|
|
/* Run diff */
|
|
|
|
snprintf(cmd, sizeof(cmd),
|
|
|
|
"diff %s \"%s\" \"%s\" >> \"%s\"",
|
|
|
|
pretty_diff_opts, best_expect_file, resultsfile, difffilename);
|
|
|
|
run_diff(cmd, difffilename);
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
unlink(diff);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-05-17 22:02:01 +02:00
|
|
|
* Wait for specified subprocesses to finish, and return their exit
|
2019-02-10 22:54:31 +01:00
|
|
|
* statuses into statuses[] and stop times into stoptimes[]
|
2006-07-20 04:10:00 +02:00
|
|
|
*
|
2008-05-17 22:02:01 +02:00
|
|
|
* If names isn't NULL, print each subprocess's name as it finishes
|
2006-07-20 04:10:00 +02:00
|
|
|
*
|
|
|
|
* Note: it's OK to scribble on the pids array, but not on the names array
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
static void
|
2019-02-10 22:54:31 +01:00
|
|
|
wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
|
|
|
|
char **names, int num_tests)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int tests_left;
|
|
|
|
int i;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-20 04:10:00 +02:00
|
|
|
#ifdef WIN32
|
2016-08-31 00:22:43 +02:00
|
|
|
PID_TYPE *active_pids = pg_malloc(num_tests * sizeof(PID_TYPE));
|
2006-07-20 04:10:00 +02:00
|
|
|
|
|
|
|
memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
|
|
|
|
#endif
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
tests_left = num_tests;
|
|
|
|
while (tests_left > 0)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
PID_TYPE p;
|
2006-07-20 04:10:00 +02:00
|
|
|
|
|
|
|
#ifndef WIN32
|
2009-01-28 16:32:21 +01:00
|
|
|
int exit_status;
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2008-05-17 22:02:01 +02:00
|
|
|
p = wait(&exit_status);
|
2006-07-20 04:10:00 +02:00
|
|
|
|
|
|
|
if (p == INVALID_PID)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
|
|
|
|
strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-20 04:10:00 +02:00
|
|
|
}
|
|
|
|
#else
|
2009-01-28 16:32:21 +01:00
|
|
|
DWORD exit_status;
|
2006-10-04 02:30:14 +02:00
|
|
|
int r;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2006-07-20 04:10:00 +02:00
|
|
|
r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
|
|
|
|
if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2011-08-23 21:00:52 +02:00
|
|
|
fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"),
|
2006-07-20 04:10:00 +02:00
|
|
|
GetLastError());
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2006-07-20 04:10:00 +02:00
|
|
|
p = active_pids[r - WAIT_OBJECT_0];
|
|
|
|
/* compact the active_pids array */
|
|
|
|
active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
|
|
|
#endif /* WIN32 */
|
2006-07-20 04:10:00 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
for (i = 0; i < num_tests; i++)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
if (p == pids[i])
|
|
|
|
{
|
2006-07-20 04:10:00 +02:00
|
|
|
#ifdef WIN32
|
2009-01-28 16:32:21 +01:00
|
|
|
GetExitCodeProcess(pids[i], &exit_status);
|
2006-07-20 04:10:00 +02:00
|
|
|
CloseHandle(pids[i]);
|
|
|
|
#endif
|
|
|
|
pids[i] = INVALID_PID;
|
2009-01-28 16:32:21 +01:00
|
|
|
statuses[i] = (int) exit_status;
|
2019-02-10 22:54:31 +01:00
|
|
|
INSTR_TIME_SET_CURRENT(stoptimes[i]);
|
2006-07-20 04:10:00 +02:00
|
|
|
if (names)
|
|
|
|
status(" %s", names[i]);
|
2006-07-19 04:37:00 +02:00
|
|
|
tests_left--;
|
2006-07-20 04:10:00 +02:00
|
|
|
break;
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 04:10:00 +02:00
|
|
|
#ifdef WIN32
|
|
|
|
free(active_pids);
|
2006-07-19 04:37:00 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2008-05-17 22:02:01 +02:00
|
|
|
/*
|
|
|
|
* 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));
|
|
|
|
#else
|
Modernize our code for looking up descriptive strings for Unix signals.
At least as far back as the 2008 spec, POSIX has defined strsignal(3)
for looking up descriptive strings for signal numbers. We hadn't gotten
the word though, and were still using the crufty old sys_siglist array,
which is in no standard even though most Unixen provide it.
Aside from not being formally standards-compliant, this was just plain
ugly because it involved #ifdef's at every place using the code.
To eliminate the #ifdef's, create a portability function pg_strsignal,
which wraps strsignal(3) if available and otherwise falls back to
sys_siglist[] if available. The set of Unixen with neither API is
probably empty these days, but on any platform with neither, you'll
just get "unrecognized signal". All extant callers print the numeric
signal number too, so no need to work harder than that.
Along the way, upgrade pg_basebackup's child-error-exit reporting
to match the rest of the system.
Discussion: https://postgr.es/m/25758.1544983503@sss.pgh.pa.us
2018-12-17 01:38:57 +01:00
|
|
|
status(_(" (test process was terminated by signal %d: %s)"),
|
|
|
|
WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
|
2008-05-17 22:02:01 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
status(_(" (test process exited with unrecognized status %d)"),
|
|
|
|
exitstatus);
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Run all the tests specified in one schedule file
|
|
|
|
*/
|
|
|
|
static void
|
2007-06-12 13:07:34 +02:00
|
|
|
run_schedule(const char *schedule, test_function tfunc)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
#define MAX_PARALLEL_TESTS 100
|
2006-10-04 02:30:14 +02:00
|
|
|
char *tests[MAX_PARALLEL_TESTS];
|
2007-06-12 13:07:34 +02:00
|
|
|
_stringlist *resultfiles[MAX_PARALLEL_TESTS];
|
|
|
|
_stringlist *expectfiles[MAX_PARALLEL_TESTS];
|
|
|
|
_stringlist *tags[MAX_PARALLEL_TESTS];
|
2006-10-04 02:30:14 +02:00
|
|
|
PID_TYPE pids[MAX_PARALLEL_TESTS];
|
2019-02-10 22:54:31 +01:00
|
|
|
instr_time starttimes[MAX_PARALLEL_TESTS];
|
|
|
|
instr_time stoptimes[MAX_PARALLEL_TESTS];
|
2008-05-17 22:02:01 +02:00
|
|
|
int statuses[MAX_PARALLEL_TESTS];
|
2006-07-19 04:37:00 +02:00
|
|
|
_stringlist *ignorelist = NULL;
|
2006-10-04 02:30:14 +02:00
|
|
|
char scbuf[1024];
|
|
|
|
FILE *scf;
|
|
|
|
int line_num = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2017-10-08 00:04:25 +02:00
|
|
|
memset(tests, 0, sizeof(tests));
|
2017-10-07 23:20:09 +02:00
|
|
|
memset(resultfiles, 0, sizeof(resultfiles));
|
|
|
|
memset(expectfiles, 0, sizeof(expectfiles));
|
|
|
|
memset(tags, 0, sizeof(tags));
|
2007-06-12 13:07:34 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
scf = fopen(schedule, "r");
|
|
|
|
if (!scf)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
|
|
|
|
progname, schedule, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
2006-07-19 18:23:17 +02:00
|
|
|
while (fgets(scbuf, sizeof(scbuf), scf))
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char *test = NULL;
|
|
|
|
char *c;
|
|
|
|
int num_tests;
|
|
|
|
bool inword;
|
|
|
|
int i;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
line_num++;
|
|
|
|
|
|
|
|
/* strip trailing whitespace, especially the newline */
|
|
|
|
i = strlen(scbuf);
|
2006-10-04 02:30:14 +02:00
|
|
|
while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
|
2006-07-19 04:37:00 +02:00
|
|
|
scbuf[--i] = '\0';
|
|
|
|
|
|
|
|
if (scbuf[0] == '\0' || scbuf[0] == '#')
|
|
|
|
continue;
|
|
|
|
if (strncmp(scbuf, "test: ", 6) == 0)
|
|
|
|
test = scbuf + 6;
|
|
|
|
else if (strncmp(scbuf, "ignore: ", 8) == 0)
|
|
|
|
{
|
|
|
|
c = scbuf + 8;
|
|
|
|
while (*c && isspace((unsigned char) *c))
|
|
|
|
c++;
|
|
|
|
add_stringlist_item(&ignorelist, c);
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Note: ignore: lines do not run the test, they just say that
|
2006-10-04 02:30:14 +02:00
|
|
|
* failure of this test when run later on is to be ignored. A bit
|
|
|
|
* odd but that's how the shell-script version did it.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
|
|
|
|
schedule, line_num, scbuf);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
num_tests = 0;
|
|
|
|
inword = false;
|
2017-10-08 00:04:25 +02:00
|
|
|
for (c = test;; c++)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2017-10-08 00:04:25 +02:00
|
|
|
if (*c == '\0' || isspace((unsigned char) *c))
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2017-10-08 00:04:25 +02:00
|
|
|
if (inword)
|
|
|
|
{
|
|
|
|
/* Reached end of a test name */
|
|
|
|
char sav;
|
|
|
|
|
|
|
|
if (num_tests >= MAX_PARALLEL_TESTS)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
|
|
|
|
MAX_PARALLEL_TESTS, schedule, line_num, scbuf);
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
sav = *c;
|
|
|
|
*c = '\0';
|
|
|
|
tests[num_tests] = pg_strdup(test);
|
|
|
|
num_tests++;
|
|
|
|
*c = sav;
|
|
|
|
inword = false;
|
|
|
|
}
|
|
|
|
if (*c == '\0')
|
|
|
|
break; /* loop exit is here */
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
else if (!inword)
|
|
|
|
{
|
2017-10-08 00:04:25 +02:00
|
|
|
/* Start of a test name */
|
|
|
|
test = c;
|
2006-07-19 04:37:00 +02:00
|
|
|
inword = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_tests == 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
|
|
|
|
schedule, line_num, scbuf);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (num_tests == 1)
|
|
|
|
{
|
2018-03-01 11:45:41 +01:00
|
|
|
status(_("test %-28s ... "), tests[0]);
|
2007-11-15 22:14:46 +01:00
|
|
|
pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
|
2019-02-10 22:54:31 +01:00
|
|
|
INSTR_TIME_SET_CURRENT(starttimes[0]);
|
|
|
|
wait_for_tests(pids, statuses, stoptimes, NULL, 1);
|
2006-07-19 04:37:00 +02:00
|
|
|
/* status line is finished below */
|
|
|
|
}
|
2017-10-07 23:20:09 +02:00
|
|
|
else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests)
|
|
|
|
{
|
2017-10-08 00:04:25 +02:00
|
|
|
fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
|
|
|
|
max_concurrent_tests, schedule, line_num, scbuf);
|
2017-10-07 23:20:09 +02:00
|
|
|
exit(2);
|
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
else if (max_connections > 0 && max_connections < num_tests)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
int oldest = 0;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
status(_("parallel group (%d tests, in groups of %d): "),
|
|
|
|
num_tests, max_connections);
|
|
|
|
for (i = 0; i < num_tests; i++)
|
|
|
|
{
|
|
|
|
if (i - oldest >= max_connections)
|
|
|
|
{
|
2008-05-17 22:02:01 +02:00
|
|
|
wait_for_tests(pids + oldest, statuses + oldest,
|
2019-02-10 22:54:31 +01:00
|
|
|
stoptimes + oldest,
|
2008-05-17 22:02:01 +02:00
|
|
|
tests + oldest, i - oldest);
|
2006-07-19 04:37:00 +02:00
|
|
|
oldest = i;
|
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
|
2019-02-10 22:54:31 +01:00
|
|
|
INSTR_TIME_SET_CURRENT(starttimes[i]);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2008-05-17 22:02:01 +02:00
|
|
|
wait_for_tests(pids + oldest, statuses + oldest,
|
2019-02-10 22:54:31 +01:00
|
|
|
stoptimes + oldest,
|
2008-05-17 22:02:01 +02:00
|
|
|
tests + oldest, i - oldest);
|
2006-07-19 04:37:00 +02:00
|
|
|
status_end();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status(_("parallel group (%d tests): "), num_tests);
|
|
|
|
for (i = 0; i < num_tests; i++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
|
2019-02-10 22:54:31 +01:00
|
|
|
INSTR_TIME_SET_CURRENT(starttimes[i]);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
2019-02-10 22:54:31 +01:00
|
|
|
wait_for_tests(pids, statuses, stoptimes, tests, num_tests);
|
2006-07-19 04:37:00 +02:00
|
|
|
status_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check results for all tests */
|
|
|
|
for (i = 0; i < num_tests; i++)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
_stringlist *rl,
|
|
|
|
*el,
|
|
|
|
*tl;
|
|
|
|
bool differ = false;
|
2007-06-12 13:07:34 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
if (num_tests > 1)
|
2018-03-01 11:45:41 +01:00
|
|
|
status(_(" %-28s ... "), tests[i]);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Advance over all three lists simultaneously.
|
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* 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.
|
2007-06-12 13:07:34 +02:00
|
|
|
*/
|
|
|
|
for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
|
2007-11-15 22:14:46 +01:00
|
|
|
rl != NULL; /* rl and el have the same length */
|
2018-04-30 03:56:27 +02:00
|
|
|
rl = rl->next, el = el->next,
|
|
|
|
tl = tl ? tl->next : NULL)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
bool newdiff;
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
newdiff = results_differ(tests[i], rl->str, el->str);
|
2007-11-15 22:14:46 +01:00
|
|
|
if (newdiff && tl)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
printf("%s ", tl->str);
|
|
|
|
}
|
|
|
|
differ |= newdiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (differ)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
bool ignore = false;
|
2006-07-19 04:37:00 +02:00
|
|
|
_stringlist *sl;
|
|
|
|
|
|
|
|
for (sl = ignorelist; sl != NULL; sl = sl->next)
|
|
|
|
{
|
|
|
|
if (strcmp(tests[i], sl->str) == 0)
|
|
|
|
{
|
|
|
|
ignore = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ignore)
|
|
|
|
{
|
|
|
|
status(_("failed (ignored)"));
|
|
|
|
fail_ignore_count++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status(_("FAILED"));
|
|
|
|
fail_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-03-25 10:00:11 +01:00
|
|
|
status(_("ok ")); /* align with FAILED */
|
2006-07-19 04:37:00 +02:00
|
|
|
success_count++;
|
|
|
|
}
|
|
|
|
|
2008-05-17 22:02:01 +02:00
|
|
|
if (statuses[i] != 0)
|
|
|
|
log_child_failure(statuses[i]);
|
|
|
|
|
2019-02-10 22:54:31 +01:00
|
|
|
INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]);
|
2019-02-18 17:16:39 +01:00
|
|
|
status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptimes[i]));
|
2019-02-10 22:54:31 +01:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
status_end();
|
|
|
|
}
|
2017-10-08 00:04:25 +02:00
|
|
|
|
|
|
|
for (i = 0; i < num_tests; i++)
|
|
|
|
{
|
|
|
|
pg_free(tests[i]);
|
|
|
|
tests[i] = NULL;
|
|
|
|
free_stringlist(&resultfiles[i]);
|
|
|
|
free_stringlist(&expectfiles[i]);
|
|
|
|
free_stringlist(&tags[i]);
|
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
2013-11-13 16:01:06 +01:00
|
|
|
free_stringlist(&ignorelist);
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
fclose(scf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Run a single test
|
|
|
|
*/
|
|
|
|
static void
|
2007-06-12 13:07:34 +02:00
|
|
|
run_single_test(const char *test, test_function tfunc)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
PID_TYPE pid;
|
2019-02-10 22:54:31 +01:00
|
|
|
instr_time starttime;
|
|
|
|
instr_time stoptime;
|
2008-05-17 22:02:01 +02:00
|
|
|
int exit_status;
|
2007-06-12 15:26:45 +02:00
|
|
|
_stringlist *resultfiles = NULL;
|
|
|
|
_stringlist *expectfiles = NULL;
|
|
|
|
_stringlist *tags = NULL;
|
2007-11-15 22:14:46 +01:00
|
|
|
_stringlist *rl,
|
|
|
|
*el,
|
|
|
|
*tl;
|
2007-06-12 13:07:34 +02:00
|
|
|
bool differ = false;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2018-03-01 11:45:41 +01:00
|
|
|
status(_("test %-28s ... "), test);
|
2007-11-15 22:14:46 +01:00
|
|
|
pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
|
2019-02-10 22:54:31 +01:00
|
|
|
INSTR_TIME_SET_CURRENT(starttime);
|
|
|
|
wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* Advance over all three lists simultaneously.
|
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* 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.
|
2007-06-12 13:07:34 +02:00
|
|
|
*/
|
|
|
|
for (rl = resultfiles, el = expectfiles, tl = tags;
|
2007-11-15 22:14:46 +01:00
|
|
|
rl != NULL; /* rl and el have the same length */
|
2018-04-30 03:56:27 +02:00
|
|
|
rl = rl->next, el = el->next,
|
|
|
|
tl = tl ? tl->next : NULL)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
bool newdiff;
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
newdiff = results_differ(test, rl->str, el->str);
|
2007-11-15 22:14:46 +01:00
|
|
|
if (newdiff && tl)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
printf("%s ", tl->str);
|
|
|
|
}
|
|
|
|
differ |= newdiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (differ)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
status(_("FAILED"));
|
|
|
|
fail_count++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-03-25 10:00:11 +01:00
|
|
|
status(_("ok ")); /* align with FAILED */
|
2006-07-19 04:37:00 +02:00
|
|
|
success_count++;
|
|
|
|
}
|
2008-05-17 22:02:01 +02:00
|
|
|
|
|
|
|
if (exit_status != 0)
|
|
|
|
log_child_failure(exit_status);
|
|
|
|
|
2019-02-10 22:54:31 +01:00
|
|
|
INSTR_TIME_SUBTRACT(stoptime, starttime);
|
2019-02-18 17:16:39 +01:00
|
|
|
status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptime));
|
2019-02-10 22:54:31 +01:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
status_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the summary-output files (making them empty if already existing)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
open_result_files(void)
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
char file[MAXPGPATH];
|
|
|
|
FILE *difffile;
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2017-03-14 23:56:17 +01:00
|
|
|
/* create outputdir directory if not present */
|
|
|
|
if (!directory_exists(outputdir))
|
|
|
|
make_directory(outputdir);
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/* create the log file (copy of running status output) */
|
|
|
|
snprintf(file, sizeof(file), "%s/regression.out", outputdir);
|
2016-08-31 00:22:43 +02:00
|
|
|
logfilename = pg_strdup(file);
|
2006-07-19 04:37:00 +02:00
|
|
|
logfile = fopen(logfilename, "w");
|
|
|
|
if (!logfile)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
|
|
|
|
progname, logfilename, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* create the diffs file as empty */
|
|
|
|
snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
|
2016-08-31 00:22:43 +02:00
|
|
|
difffilename = pg_strdup(file);
|
2006-07-19 04:37:00 +02:00
|
|
|
difffile = fopen(difffilename, "w");
|
|
|
|
if (!difffile)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
|
|
|
|
progname, difffilename, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
/* we don't keep the diffs file open continuously */
|
|
|
|
fclose(difffile);
|
|
|
|
|
2017-03-14 23:56:17 +01:00
|
|
|
/* also create the results directory if not present */
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(file, sizeof(file), "%s/results", outputdir);
|
|
|
|
if (!directory_exists(file))
|
|
|
|
make_directory(file);
|
|
|
|
}
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
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;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* We use template0 so that any installation-local cruft in template1 will
|
|
|
|
* not mess up the tests.
|
|
|
|
*/
|
|
|
|
header(_("creating database \"%s\""), dbname);
|
|
|
|
if (encoding)
|
2009-08-18 12:30:41 +02:00
|
|
|
psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
|
|
|
|
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
|
2007-06-12 13:07:34 +02:00
|
|
|
else
|
2009-08-18 12:30:41 +02:00
|
|
|
psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
|
|
|
|
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
|
2007-11-15 22:14:46 +01:00
|
|
|
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';"
|
2017-03-20 23:12:24 +01:00
|
|
|
"ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
"ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
|
2017-03-20 23:12:24 +01:00
|
|
|
dbname, dbname, dbname, dbname, dbname, dbname);
|
2007-06-12 13:07:34 +02:00
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Install any requested procedural languages. We use CREATE OR REPLACE
|
2010-02-24 02:35:14 +01:00
|
|
|
* so that this will work whether or not the language is preinstalled.
|
2007-06-12 13:07:34 +02:00
|
|
|
*/
|
|
|
|
for (sl = loadlanguage; sl != NULL; sl = sl->next)
|
|
|
|
{
|
|
|
|
header(_("installing %s"), sl->str);
|
2010-02-24 02:35:14 +01:00
|
|
|
psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
|
2007-06-12 13:07:34 +02:00
|
|
|
}
|
2011-03-05 03:51:14 +01:00
|
|
|
|
|
|
|
/*
|
2011-04-10 17:42:00 +02:00
|
|
|
* Install any requested extensions. We use CREATE IF NOT EXISTS so that
|
|
|
|
* this will work whether or not the extension is preinstalled.
|
2011-03-05 03:51:14 +01:00
|
|
|
*/
|
|
|
|
for (sl = loadextension; sl != NULL; sl = sl->next)
|
|
|
|
{
|
|
|
|
header(_("installing %s"), sl->str);
|
|
|
|
psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
|
|
|
|
}
|
2007-06-12 13:07:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2014-05-06 18:12:18 +02:00
|
|
|
create_role(const char *rolename, const _stringlist *granted_dbs)
|
2007-06-12 13:07:34 +02:00
|
|
|
{
|
|
|
|
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\"",
|
2007-11-15 22:14:46 +01:00
|
|
|
granted_dbs->str, rolename);
|
2007-06-12 13:07:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
static void
|
|
|
|
help(void)
|
|
|
|
{
|
|
|
|
printf(_("PostgreSQL regression test driver\n"));
|
|
|
|
printf(_("\n"));
|
2011-08-30 20:25:10 +02:00
|
|
|
printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
|
2006-07-19 04:37:00 +02:00
|
|
|
printf(_("\n"));
|
|
|
|
printf(_("Options:\n"));
|
2017-10-14 01:06:41 +02:00
|
|
|
printf(_(" --bindir=BINPATH use BINPATH for programs that are run;\n"));
|
|
|
|
printf(_(" if empty, use PATH from the environment\n"));
|
|
|
|
printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
|
|
|
|
printf(_(" --create-role=ROLE create the specified role before testing\n"));
|
|
|
|
printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
|
|
|
|
printf(_(" --debug turn on debug mode in programs that are run\n"));
|
|
|
|
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
|
|
|
|
printf(_(" --encoding=ENCODING use ENCODING as the encoding\n"));
|
|
|
|
printf(_(" -h, --help show this help, then exit\n"));
|
|
|
|
printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n"));
|
|
|
|
printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
|
|
|
|
printf(_(" --load-extension=EXT load the named extension before running the\n"));
|
|
|
|
printf(_(" tests; can appear multiple times\n"));
|
|
|
|
printf(_(" --load-language=LANG load the named language before running the\n"));
|
|
|
|
printf(_(" tests; can appear multiple times\n"));
|
|
|
|
printf(_(" --max-connections=N maximum number of concurrent connections\n"));
|
|
|
|
printf(_(" (default is 0, meaning unlimited)\n"));
|
|
|
|
printf(_(" --max-concurrent-tests=N maximum number of concurrent tests in schedule\n"));
|
|
|
|
printf(_(" (default is 0, meaning unlimited)\n"));
|
|
|
|
printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n"));
|
|
|
|
printf(_(" --schedule=FILE use test ordering schedule from FILE\n"));
|
|
|
|
printf(_(" (can be used multiple times to concatenate)\n"));
|
|
|
|
printf(_(" --temp-instance=DIR create a temporary instance in DIR\n"));
|
|
|
|
printf(_(" --use-existing use an existing installation\n"));
|
|
|
|
printf(_(" -V, --version output version information, then exit\n"));
|
2015-06-05 23:04:07 +02:00
|
|
|
printf(_("\n"));
|
2015-04-23 14:59:52 +02:00
|
|
|
printf(_("Options for \"temp-instance\" mode:\n"));
|
2017-10-14 01:06:41 +02:00
|
|
|
printf(_(" --no-locale use C locale\n"));
|
|
|
|
printf(_(" --port=PORT start postmaster on PORT\n"));
|
|
|
|
printf(_(" --temp-config=FILE append contents of FILE to temporary config\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
printf(_("\n"));
|
|
|
|
printf(_("Options for using an existing installation:\n"));
|
2017-10-14 01:06:41 +02:00
|
|
|
printf(_(" --host=HOST use postmaster running on HOST\n"));
|
|
|
|
printf(_(" --port=PORT use postmaster running at PORT\n"));
|
|
|
|
printf(_(" --user=USER connect as USER\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
printf(_("\n"));
|
|
|
|
printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
|
|
|
|
printf(_("if the tests could not be run for some reason.\n"));
|
|
|
|
printf(_("\n"));
|
2019-01-19 19:06:35 +01:00
|
|
|
printf(_("Report bugs to <pgsql-bugs@lists.postgresql.org>.\n"));
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-06-12 13:07:34 +02:00
|
|
|
regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"help", no_argument, NULL, 'h'},
|
|
|
|
{"version", no_argument, NULL, 'V'},
|
|
|
|
{"dbname", required_argument, NULL, 1},
|
|
|
|
{"debug", no_argument, NULL, 2},
|
|
|
|
{"inputdir", required_argument, NULL, 3},
|
|
|
|
{"load-language", required_argument, NULL, 4},
|
|
|
|
{"max-connections", required_argument, NULL, 5},
|
2011-04-15 07:42:05 +02:00
|
|
|
{"encoding", required_argument, NULL, 6},
|
2006-07-19 04:37:00 +02:00
|
|
|
{"outputdir", required_argument, NULL, 7},
|
|
|
|
{"schedule", required_argument, NULL, 8},
|
2015-04-23 14:59:52 +02:00
|
|
|
{"temp-instance", required_argument, NULL, 9},
|
2006-07-19 04:37:00 +02:00
|
|
|
{"no-locale", no_argument, NULL, 10},
|
|
|
|
{"host", required_argument, NULL, 13},
|
|
|
|
{"port", required_argument, NULL, 14},
|
|
|
|
{"user", required_argument, NULL, 15},
|
2015-04-23 14:59:52 +02:00
|
|
|
{"bindir", required_argument, NULL, 16},
|
2008-10-02 00:38:57 +02:00
|
|
|
{"dlpath", required_argument, NULL, 17},
|
2007-06-12 13:07:34 +02:00
|
|
|
{"create-role", required_argument, NULL, 18},
|
2007-09-09 22:40:54 +02:00
|
|
|
{"temp-config", required_argument, NULL, 19},
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 02:32:45 +01:00
|
|
|
{"use-existing", no_argument, NULL, 20},
|
2011-01-24 02:44:48 +01:00
|
|
|
{"launcher", required_argument, NULL, 21},
|
2011-03-05 03:51:14 +01:00
|
|
|
{"load-extension", required_argument, NULL, 22},
|
2014-12-18 04:48:40 +01:00
|
|
|
{"config-auth", required_argument, NULL, 24},
|
2017-10-07 23:20:09 +02:00
|
|
|
{"max-concurrent-tests", required_argument, NULL, 25},
|
2006-07-19 04:37:00 +02:00
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
2012-11-30 20:49:55 +01:00
|
|
|
_stringlist *sl;
|
|
|
|
int c;
|
|
|
|
int i;
|
|
|
|
int option_index;
|
|
|
|
char buf[MAXPGPATH * 4];
|
|
|
|
char buf2[MAXPGPATH * 4];
|
|
|
|
|
Unified logging system for command-line programs
This unifies the various ad hoc logging (message printing, error
printing) systems used throughout the command-line programs.
Features:
- Program name is automatically prefixed.
- Message string does not end with newline. This removes a common
source of inconsistencies and omissions.
- Additionally, a final newline is automatically stripped, simplifying
use of PQerrorMessage() etc., another common source of mistakes.
- I converted error message strings to use %m where possible.
- As a result of the above several points, more translatable message
strings can be shared between different components and between
frontends and backend, without gratuitous punctuation or whitespace
differences.
- There is support for setting a "log level". This is not meant to be
user-facing, but can be used internally to implement debug or
verbose modes.
- Lazy argument evaluation, so no significant overhead if logging at
some level is disabled.
- Some color in the messages, similar to gcc and clang. Set
PG_COLOR=auto to try it out. Some colors are predefined, but can be
customized by setting PG_COLORS.
- Common files (common/, fe_utils/, etc.) can handle logging much more
simply by just using one API without worrying too much about the
context of the calling program, requiring callbacks, or having to
pass "progname" around everywhere.
- Some programs called setvbuf() to make sure that stderr is
unbuffered, even on Windows. But not all programs did that. This
is now done centrally.
Soft goals:
- Reduces vertical space use and visual complexity of error reporting
in the source code.
- Encourages more deliberate classification of messages. For example,
in some cases it wasn't clear without analyzing the surrounding code
whether a message was meant as an error or just an info.
- Concepts and terms are vaguely aligned with popular logging
frameworks such as log4j and Python logging.
This is all just about printing stuff out. Nothing affects program
flow (e.g., fatal exits). The uses are just too varied to do that.
Some existing code had wrappers that do some kind of print-and-exit,
and I adapted those.
I tried to keep the output mostly the same, but there is a lot of
historical baggage to unwind and special cases to consider, and I
might not always have succeeded. One significant change is that
pg_rewind used to write all error messages to stdout. That is now
changed to stderr.
Reviewed-by: Donald Dong <xdong@csumb.edu>
Reviewed-by: Arthur Zakirov <a.zakirov@postgrespro.ru>
Discussion: https://www.postgresql.org/message-id/flat/6a609b43-4f57-7348-6480-bd022f924310@2ndquadrant.com
2019-04-01 14:24:37 +02:00
|
|
|
pg_logging_init(argv[0]);
|
2006-07-19 04:37:00 +02:00
|
|
|
progname = get_progname(argv[0]);
|
2008-12-11 08:34:09 +01:00
|
|
|
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
|
2006-07-19 04:37:00 +02:00
|
|
|
|
Unified logging system for command-line programs
This unifies the various ad hoc logging (message printing, error
printing) systems used throughout the command-line programs.
Features:
- Program name is automatically prefixed.
- Message string does not end with newline. This removes a common
source of inconsistencies and omissions.
- Additionally, a final newline is automatically stripped, simplifying
use of PQerrorMessage() etc., another common source of mistakes.
- I converted error message strings to use %m where possible.
- As a result of the above several points, more translatable message
strings can be shared between different components and between
frontends and backend, without gratuitous punctuation or whitespace
differences.
- There is support for setting a "log level". This is not meant to be
user-facing, but can be used internally to implement debug or
verbose modes.
- Lazy argument evaluation, so no significant overhead if logging at
some level is disabled.
- Some color in the messages, similar to gcc and clang. Set
PG_COLOR=auto to try it out. Some colors are predefined, but can be
customized by setting PG_COLORS.
- Common files (common/, fe_utils/, etc.) can handle logging much more
simply by just using one API without worrying too much about the
context of the calling program, requiring callbacks, or having to
pass "progname" around everywhere.
- Some programs called setvbuf() to make sure that stderr is
unbuffered, even on Windows. But not all programs did that. This
is now done centrally.
Soft goals:
- Reduces vertical space use and visual complexity of error reporting
in the source code.
- Encourages more deliberate classification of messages. For example,
in some cases it wasn't clear without analyzing the surrounding code
whether a message was meant as an error or just an info.
- Concepts and terms are vaguely aligned with popular logging
frameworks such as log4j and Python logging.
This is all just about printing stuff out. Nothing affects program
flow (e.g., fatal exits). The uses are just too varied to do that.
Some existing code had wrappers that do some kind of print-and-exit,
and I adapted those.
I tried to keep the output mostly the same, but there is a lot of
historical baggage to unwind and special cases to consider, and I
might not always have succeeded. One significant change is that
pg_rewind used to write all error messages to stdout. That is now
changed to stderr.
Reviewed-by: Donald Dong <xdong@csumb.edu>
Reviewed-by: Arthur Zakirov <a.zakirov@postgrespro.ru>
Discussion: https://www.postgresql.org/message-id/flat/6a609b43-4f57-7348-6480-bd022f924310@2ndquadrant.com
2019-04-01 14:24:37 +02:00
|
|
|
get_restricted_token();
|
2018-10-20 15:02:36 +02:00
|
|
|
|
2012-01-02 21:08:04 +01:00
|
|
|
atexit(stop_postmaster);
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
#ifndef HAVE_UNIX_SOCKETS
|
|
|
|
/* no unix domain sockets available, so change default */
|
|
|
|
hostname = "localhost";
|
|
|
|
#endif
|
|
|
|
|
2007-06-12 13:07:34 +02:00
|
|
|
/*
|
|
|
|
* We call the initialization function here because that way we can set
|
|
|
|
* default parameters and let them be overwritten by the commandline.
|
|
|
|
*/
|
2013-11-08 20:40:41 +01:00
|
|
|
ifunc(argc, argv);
|
2007-06-12 13:07:34 +02:00
|
|
|
|
2013-01-30 04:58:38 +01:00
|
|
|
if (getenv("PG_REGRESS_DIFF_OPTS"))
|
|
|
|
pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
|
|
|
|
{
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case 'h':
|
|
|
|
help();
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(0);
|
2006-07-19 04:37:00 +02:00
|
|
|
case 'V':
|
2008-02-20 23:44:16 +01:00
|
|
|
puts("pg_regress (PostgreSQL) " PG_VERSION);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(0);
|
2006-07-19 04:37:00 +02:00
|
|
|
case 1:
|
2007-11-15 22:14:46 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If a default database was specified, we need to remove it
|
|
|
|
* before we add the specified one.
|
2007-06-12 15:54:58 +02:00
|
|
|
*/
|
|
|
|
free_stringlist(&dblist);
|
2016-08-04 20:44:23 +02:00
|
|
|
split_to_stringlist(optarg, ",", &dblist);
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
debug = true;
|
|
|
|
break;
|
|
|
|
case 3:
|
2016-08-31 00:22:43 +02:00
|
|
|
inputdir = pg_strdup(optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
add_stringlist_item(&loadlanguage, optarg);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
max_connections = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 6:
|
2016-08-31 00:22:43 +02:00
|
|
|
encoding = pg_strdup(optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
case 7:
|
2016-08-31 00:22:43 +02:00
|
|
|
outputdir = pg_strdup(optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
add_stringlist_item(&schedulelist, optarg);
|
|
|
|
break;
|
|
|
|
case 9:
|
2015-04-23 14:59:52 +02:00
|
|
|
temp_instance = make_absolute_path(optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
nolocale = true;
|
|
|
|
break;
|
|
|
|
case 13:
|
2016-08-31 00:22:43 +02:00
|
|
|
hostname = pg_strdup(optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
port = atoi(optarg);
|
2008-11-28 13:45:34 +01:00
|
|
|
port_specified_by_user = true;
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
case 15:
|
2016-08-31 00:22:43 +02:00
|
|
|
user = pg_strdup(optarg);
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
2006-07-21 02:24:04 +02:00
|
|
|
case 16:
|
2015-04-23 14:59:52 +02:00
|
|
|
/* "--bindir=" means to use PATH */
|
2006-07-21 02:24:04 +02:00
|
|
|
if (strlen(optarg))
|
2016-08-31 00:22:43 +02:00
|
|
|
bindir = pg_strdup(optarg);
|
2015-04-23 14:59:52 +02:00
|
|
|
else
|
|
|
|
bindir = NULL;
|
2006-07-21 02:24:04 +02:00
|
|
|
break;
|
2007-01-19 17:42:24 +01:00
|
|
|
case 17:
|
2016-08-31 00:22:43 +02:00
|
|
|
dlpath = pg_strdup(optarg);
|
2007-01-19 17:42:24 +01:00
|
|
|
break;
|
2007-06-12 13:07:34 +02:00
|
|
|
case 18:
|
2016-08-04 20:44:23 +02:00
|
|
|
split_to_stringlist(optarg, ",", &extraroles);
|
2007-06-12 13:07:34 +02:00
|
|
|
break;
|
2007-09-09 22:40:54 +02:00
|
|
|
case 19:
|
2016-02-28 15:38:43 +01:00
|
|
|
add_stringlist_item(&temp_configs, optarg);
|
2007-09-09 22:40:54 +02:00
|
|
|
break;
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 02:32:45 +01:00
|
|
|
case 20:
|
|
|
|
use_existing = true;
|
|
|
|
break;
|
2011-01-24 02:44:48 +01:00
|
|
|
case 21:
|
2016-08-31 00:22:43 +02:00
|
|
|
launcher = pg_strdup(optarg);
|
2011-01-24 02:44:48 +01:00
|
|
|
break;
|
2011-03-05 03:51:14 +01:00
|
|
|
case 22:
|
|
|
|
add_stringlist_item(&loadextension, optarg);
|
|
|
|
break;
|
2014-12-18 04:48:40 +01:00
|
|
|
case 24:
|
2016-08-31 00:22:43 +02:00
|
|
|
config_auth_datadir = pg_strdup(optarg);
|
2014-12-18 04:48:40 +01:00
|
|
|
break;
|
2017-10-07 23:20:09 +02:00
|
|
|
case 25:
|
|
|
|
max_concurrent_tests = atoi(optarg);
|
|
|
|
break;
|
2006-07-19 04:37:00 +02:00
|
|
|
default:
|
|
|
|
/* getopt_long already emitted a complaint */
|
|
|
|
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
|
|
|
|
progname);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if we still have arguments, they are extra tests to run
|
|
|
|
*/
|
|
|
|
while (argc - optind >= 1)
|
|
|
|
{
|
|
|
|
add_stringlist_item(&extra_tests, argv[optind]);
|
|
|
|
optind++;
|
|
|
|
}
|
|
|
|
|
2014-12-18 04:48:40 +01:00
|
|
|
if (config_auth_datadir)
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_SSPI
|
|
|
|
config_sspi_auth(config_auth_datadir);
|
|
|
|
#endif
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2015-04-23 14:59:52 +02:00
|
|
|
if (temp_instance && !port_specified_by_user)
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2008-11-28 13:45:34 +01:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* To reduce chances of interference with parallel installations, use
|
|
|
|
* a port number starting in the private range (49152-65535)
|
2014-06-14 15:41:13 +02:00
|
|
|
* calculated from the version number. This aids !HAVE_UNIX_SOCKETS
|
|
|
|
* systems; elsewhere, the use of a private socket directory already
|
|
|
|
* prevents interference.
|
2008-11-28 13:45:34 +01:00
|
|
|
*/
|
|
|
|
port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2019-02-23 09:37:25 +01:00
|
|
|
inputdir = make_absolute_path(inputdir);
|
|
|
|
outputdir = make_absolute_path(outputdir);
|
|
|
|
dlpath = make_absolute_path(dlpath);
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Initialization
|
|
|
|
*/
|
|
|
|
open_result_files();
|
|
|
|
|
|
|
|
initialize_environment();
|
|
|
|
|
2007-01-05 17:17:55 +01:00
|
|
|
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
|
|
|
|
unlimit_core_size();
|
|
|
|
#endif
|
|
|
|
|
2015-04-23 14:59:52 +02:00
|
|
|
if (temp_instance)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2009-04-23 02:23:46 +02:00
|
|
|
FILE *pg_conf;
|
2016-04-21 05:48:13 +02:00
|
|
|
const char *env_wait;
|
|
|
|
int wait_seconds;
|
2009-04-23 02:23:46 +02:00
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
2015-04-23 14:59:52 +02:00
|
|
|
* Prepare the temp instance
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
|
|
|
|
2015-04-23 14:59:52 +02:00
|
|
|
if (directory_exists(temp_instance))
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2015-04-23 14:59:52 +02:00
|
|
|
header(_("removing existing temp instance"));
|
|
|
|
if (!rmtree(temp_instance, true))
|
2014-03-02 04:14:14 +01:00
|
|
|
{
|
2015-04-23 14:59:52 +02:00
|
|
|
fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
|
|
|
|
progname, temp_instance);
|
2014-03-02 04:14:14 +01:00
|
|
|
exit(2);
|
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
2015-04-23 14:59:52 +02:00
|
|
|
header(_("creating temporary instance"));
|
2006-07-19 04:37:00 +02:00
|
|
|
|
2015-04-23 14:59:52 +02:00
|
|
|
/* make the temp instance top directory */
|
|
|
|
make_directory(temp_instance);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/* and a directory for log files */
|
2015-07-21 15:53:16 +02:00
|
|
|
snprintf(buf, sizeof(buf), "%s/log", outputdir);
|
2006-07-19 04:37:00 +02:00
|
|
|
if (!directory_exists(buf))
|
|
|
|
make_directory(buf);
|
|
|
|
|
|
|
|
/* initdb */
|
|
|
|
header(_("initializing database system"));
|
|
|
|
snprintf(buf, sizeof(buf),
|
2016-10-19 18:00:00 +02:00
|
|
|
"\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1",
|
2015-04-23 14:59:52 +02:00
|
|
|
bindir ? bindir : "",
|
|
|
|
bindir ? "/" : "",
|
|
|
|
temp_instance,
|
2006-07-20 18:25:30 +02:00
|
|
|
debug ? " --debug" : "",
|
|
|
|
nolocale ? " --no-locale" : "",
|
2015-07-21 15:53:16 +02:00
|
|
|
outputdir);
|
2006-07-19 04:37:00 +02:00
|
|
|
if (system(buf))
|
|
|
|
{
|
2015-07-21 15:53:16 +02:00
|
|
|
fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
2009-04-23 02:23:46 +02:00
|
|
|
/*
|
2015-01-18 20:08:09 +01:00
|
|
|
* Adjust the default postgresql.conf for regression testing. The user
|
|
|
|
* can specify a file to be appended; in any case we expand logging
|
|
|
|
* and set max_prepared_transactions to enable testing of prepared
|
|
|
|
* xacts. (Note: to reduce the probability of unexpected shmmax
|
|
|
|
* failures, don't set max_prepared_transactions any higher than
|
|
|
|
* actually needed by the prepared_xacts regression test.)
|
2009-04-23 02:23:46 +02:00
|
|
|
*/
|
2015-04-23 14:59:52 +02:00
|
|
|
snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
|
2009-04-23 02:23:46 +02:00
|
|
|
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));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2009-04-23 02:23:46 +02:00
|
|
|
}
|
|
|
|
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
|
2015-01-18 20:08:09 +01:00
|
|
|
fputs("log_autovacuum_min_duration = 0\n", pg_conf);
|
|
|
|
fputs("log_checkpoints = on\n", pg_conf);
|
2016-09-30 18:00:00 +02:00
|
|
|
fputs("log_line_prefix = '%m [%p] %q%a '\n", pg_conf);
|
2015-01-18 20:08:09 +01:00
|
|
|
fputs("log_lock_waits = on\n", pg_conf);
|
|
|
|
fputs("log_temp_files = 128kB\n", pg_conf);
|
2009-04-23 02:23:46 +02:00
|
|
|
fputs("max_prepared_transactions = 2\n", pg_conf);
|
|
|
|
|
2016-02-28 15:38:43 +01:00
|
|
|
for (sl = temp_configs; sl != NULL; sl = sl->next)
|
2007-09-09 22:40:54 +02:00
|
|
|
{
|
2016-02-28 15:38:43 +01:00
|
|
|
char *temp_config = sl->str;
|
2007-11-15 22:14:46 +01:00
|
|
|
FILE *extra_conf;
|
|
|
|
char line_buf[1024];
|
2007-09-09 22:40:54 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
extra_conf = fopen(temp_config, "r");
|
2007-09-09 22:40:54 +02:00
|
|
|
if (extra_conf == NULL)
|
|
|
|
{
|
2008-08-05 07:16:08 +02:00
|
|
|
fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2007-09-09 22:40:54 +02:00
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
|
2007-09-09 22:40:54 +02:00
|
|
|
fputs(line_buf, pg_conf);
|
|
|
|
fclose(extra_conf);
|
|
|
|
}
|
|
|
|
|
2009-04-23 02:23:46 +02:00
|
|
|
fclose(pg_conf);
|
|
|
|
|
2014-12-18 04:48:40 +01:00
|
|
|
#ifdef ENABLE_SSPI
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we successfully used the same buffer for the much-longer
|
|
|
|
* "initdb" command, this can't truncate.
|
|
|
|
*/
|
2015-04-23 14:59:52 +02:00
|
|
|
snprintf(buf, sizeof(buf), "%s/data", temp_instance);
|
2014-12-18 04:48:40 +01:00
|
|
|
config_sspi_auth(buf);
|
|
|
|
#elif !defined(HAVE_UNIX_SOCKETS)
|
|
|
|
#error Platform has no means to secure the test installation.
|
|
|
|
#endif
|
|
|
|
|
2008-11-28 13:45:34 +01:00
|
|
|
/*
|
|
|
|
* Check if there is a postmaster running already.
|
|
|
|
*/
|
|
|
|
snprintf(buf2, sizeof(buf2),
|
2015-04-23 14:59:52 +02:00
|
|
|
"\"%s%spsql\" -X postgres <%s 2>%s",
|
|
|
|
bindir ? bindir : "",
|
|
|
|
bindir ? "/" : "",
|
|
|
|
DEVNULL, DEVNULL);
|
2008-11-28 13:45:34 +01:00
|
|
|
|
|
|
|
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"));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2008-11-28 13:45:34 +01:00
|
|
|
}
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1);
|
2008-11-28 13:45:34 +01:00
|
|
|
port++;
|
|
|
|
sprintf(s, "%d", port);
|
|
|
|
doputenv("PGPORT", s);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
/*
|
|
|
|
* Start the temp postmaster
|
|
|
|
*/
|
|
|
|
header(_("starting postmaster"));
|
|
|
|
snprintf(buf, sizeof(buf),
|
2015-04-23 14:59:52 +02:00
|
|
|
"\"%s%spostgres\" -D \"%s/data\" -F%s "
|
2014-06-14 15:41:13 +02:00
|
|
|
"-c \"listen_addresses=%s\" -k \"%s\" "
|
|
|
|
"> \"%s/log/postmaster.log\" 2>&1",
|
2015-04-23 14:59:52 +02:00
|
|
|
bindir ? bindir : "",
|
|
|
|
bindir ? "/" : "",
|
|
|
|
temp_instance, debug ? " -d 5" : "",
|
2014-06-14 15:41:13 +02:00
|
|
|
hostname ? hostname : "", sockdir ? sockdir : "",
|
2015-07-21 15:53:16 +02:00
|
|
|
outputdir);
|
2006-07-19 04:37:00 +02:00
|
|
|
postmaster_pid = spawn_process(buf);
|
|
|
|
if (postmaster_pid == INVALID_PID)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"),
|
|
|
|
progname, strerror(errno));
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-04-21 05:48:13 +02:00
|
|
|
* Wait till postmaster is able to accept connections; normally this
|
|
|
|
* is only a second or so, but Cygwin is reportedly *much* slower, and
|
|
|
|
* test builds using Valgrind or similar tools might be too. Hence,
|
|
|
|
* allow the default timeout of 60 seconds to be overridden from the
|
|
|
|
* PGCTLTIMEOUT environment variable.
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
2016-04-21 05:48:13 +02:00
|
|
|
env_wait = getenv("PGCTLTIMEOUT");
|
|
|
|
if (env_wait != NULL)
|
|
|
|
{
|
|
|
|
wait_seconds = atoi(env_wait);
|
|
|
|
if (wait_seconds <= 0)
|
|
|
|
wait_seconds = 60;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
wait_seconds = 60;
|
|
|
|
|
|
|
|
for (i = 0; i < wait_seconds; i++)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
/* Done if psql succeeds */
|
2008-11-28 13:45:34 +01:00
|
|
|
if (system(buf2) == 0)
|
2006-07-19 04:37:00 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fail immediately if postmaster has exited
|
|
|
|
*/
|
|
|
|
#ifndef WIN32
|
2018-12-31 22:50:32 +01:00
|
|
|
if (waitpid(postmaster_pid, NULL, WNOHANG) == postmaster_pid)
|
2006-09-24 19:10:18 +02:00
|
|
|
#else
|
|
|
|
if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
|
|
|
|
#endif
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2015-07-21 15:53:16 +02:00
|
|
|
fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pg_usleep(1000000L);
|
|
|
|
}
|
2016-04-21 05:48:13 +02:00
|
|
|
if (i >= wait_seconds)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
2016-04-21 05:48:13 +02:00
|
|
|
fprintf(stderr, _("\n%s: postmaster did not respond within %d seconds\nExamine %s/log/postmaster.log for the reason\n"),
|
|
|
|
progname, wait_seconds, outputdir);
|
2006-08-13 22:39:07 +02:00
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* If we get here, the postmaster is probably wedged somewhere in
|
|
|
|
* startup. Try to kill it ungracefully rather than leaving a
|
|
|
|
* stuck postmaster that might interfere with subsequent test
|
2006-08-13 22:39:07 +02:00
|
|
|
* attempts.
|
|
|
|
*/
|
|
|
|
#ifndef WIN32
|
|
|
|
if (kill(postmaster_pid, SIGKILL) != 0 &&
|
|
|
|
errno != ESRCH)
|
|
|
|
fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n"),
|
|
|
|
progname, strerror(errno));
|
2006-09-24 19:10:18 +02:00
|
|
|
#else
|
|
|
|
if (TerminateProcess(postmaster_pid, 255) == 0)
|
2011-08-23 21:00:52 +02:00
|
|
|
fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n"),
|
2006-09-24 19:10:18 +02:00
|
|
|
progname, GetLastError());
|
2006-08-13 22:39:07 +02:00
|
|
|
#endif
|
|
|
|
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(2);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
postmaster_running = true;
|
|
|
|
|
2016-04-12 01:37:04 +02:00
|
|
|
#ifdef _WIN64
|
2011-04-19 17:21:00 +02:00
|
|
|
/* 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
|
2011-06-18 23:37:30 +02:00
|
|
|
printf(_("running on port %d with PID %lu\n"),
|
2011-04-19 17:21:00 +02:00
|
|
|
port, ULONGPID(postmaster_pid));
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Using an existing installation, so may need to get rid of
|
2007-06-12 13:07:34 +02:00
|
|
|
* pre-existing database(s) and role(s)
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 02:32:45 +01:00
|
|
|
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);
|
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-06-12 13:07:34 +02:00
|
|
|
* Create the test database(s) and role(s)
|
2006-07-19 04:37:00 +02:00
|
|
|
*/
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 02:32:45 +01:00
|
|
|
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);
|
|
|
|
}
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ready to run the tests
|
|
|
|
*/
|
|
|
|
header(_("running regression test queries"));
|
|
|
|
|
|
|
|
for (sl = schedulelist; sl != NULL; sl = sl->next)
|
|
|
|
{
|
2007-06-12 13:07:34 +02:00
|
|
|
run_schedule(sl->str, tfunc);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (sl = extra_tests; sl != NULL; sl = sl->next)
|
|
|
|
{
|
2007-06-12 13:07:34 +02:00
|
|
|
run_single_test(sl->str, tfunc);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Shut down temp installation's postmaster
|
|
|
|
*/
|
2015-04-23 14:59:52 +02:00
|
|
|
if (temp_instance)
|
2006-07-19 04:37:00 +02:00
|
|
|
{
|
|
|
|
header(_("shutting down postmaster"));
|
|
|
|
stop_postmaster();
|
|
|
|
}
|
|
|
|
|
2015-01-20 05:44:19 +01:00
|
|
|
/*
|
2015-04-23 14:59:52 +02:00
|
|
|
* If there were no errors, remove the temp instance immediately to
|
2015-05-24 03:35:49 +02:00
|
|
|
* conserve disk space. (If there were errors, we leave the instance in
|
|
|
|
* place for possible manual investigation.)
|
2015-01-20 05:44:19 +01:00
|
|
|
*/
|
2015-04-23 14:59:52 +02:00
|
|
|
if (temp_instance && fail_count == 0 && fail_ignore_count == 0)
|
2015-01-20 05:44:19 +01:00
|
|
|
{
|
2015-04-23 14:59:52 +02:00
|
|
|
header(_("removing temporary instance"));
|
|
|
|
if (!rmtree(temp_instance, true))
|
|
|
|
fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
|
|
|
|
progname, temp_instance);
|
2015-01-20 05:44:19 +01:00
|
|
|
}
|
|
|
|
|
2006-07-19 04:37:00 +02:00
|
|
|
fclose(logfile);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Emit nice-looking summary message
|
|
|
|
*/
|
|
|
|
if (fail_count == 0 && fail_ignore_count == 0)
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
_(" All %d tests passed. "),
|
|
|
|
success_count);
|
2006-10-04 02:30:14 +02:00
|
|
|
else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
_(" %d of %d tests passed, %d failed test(s) ignored. "),
|
|
|
|
success_count,
|
|
|
|
success_count + fail_ignore_count,
|
|
|
|
fail_ignore_count);
|
2006-10-04 02:30:14 +02:00
|
|
|
else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
_(" %d of %d tests failed. "),
|
|
|
|
fail_count,
|
2006-10-04 02:30:14 +02:00
|
|
|
success_count + fail_count);
|
|
|
|
else
|
|
|
|
/* fail_count>0 && fail_ignore_count>0 */
|
2006-07-19 04:37:00 +02:00
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
_(" %d of %d tests failed, %d of these failures ignored. "),
|
2006-10-04 02:30:14 +02:00
|
|
|
fail_count + fail_ignore_count,
|
|
|
|
success_count + fail_count + fail_ignore_count,
|
2006-07-19 04:37:00 +02:00
|
|
|
fail_ignore_count);
|
|
|
|
|
|
|
|
putchar('\n');
|
|
|
|
for (i = strlen(buf); i > 0; i--)
|
|
|
|
putchar('=');
|
|
|
|
printf("\n%s\n", buf);
|
|
|
|
for (i = strlen(buf); i > 0; i--)
|
|
|
|
putchar('=');
|
|
|
|
putchar('\n');
|
|
|
|
putchar('\n');
|
|
|
|
|
|
|
|
if (file_size(difffilename) > 0)
|
|
|
|
{
|
|
|
|
printf(_("The differences that caused some tests to fail can be viewed in the\n"
|
|
|
|
"file \"%s\". A copy of the test summary that you see\n"
|
|
|
|
"above is saved in the file \"%s\".\n\n"),
|
2019-02-23 09:37:25 +01:00
|
|
|
difffilename, logfilename);
|
2006-07-19 04:37:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unlink(difffilename);
|
|
|
|
unlink(logfilename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fail_count != 0)
|
2012-01-02 21:08:04 +01:00
|
|
|
exit(1);
|
2006-07-19 04:37:00 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|