#include <postgres.h>
#include <regex/regex.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "getopt_long.h"


#ifdef WIN32
#define PID_TYPE HANDLE
#define INVALID_PID INVALID_HANDLE_VALUE
#else
#define PID_TYPE pid_t
#define INVALID_PID -1
#endif

#define header(t, ...) do { char tmp[128];sprintf(tmp, t, ##__VA_ARGS__);printf("============== %-38s ==============\n",tmp); fflush(stdout); } while (0); 
#define status(a, ...) do { printf(a, ##__VA_ARGS__); fflush(stdout); if(logfile) fprintf(logfile,a,##__VA_ARGS__); } while (0);
#define status_end() do { printf("\n"); fflush(stdout); if (logfile) fprintf(logfile,"\n"); } while (0);

typedef struct _stringlist _stringlist;
struct _stringlist
{
	char *str;
	_stringlist *next;
};


static char *libdir = NULL;
static char *bindir = "";
static char *inputdir = ".";
static char *outputdir = ".";
static char *host_platform = NULL;
static char *dbname = "regression";
static char *hostname = NULL;
static char *user = NULL;
static bool nolocale = false;
static bool debug = false;
static int port = -1;
static int tempport = 65432;
static char *schedule = "";
static char *encoding = "SQL_ASCII";
static _stringlist *loadlanguage = NULL;
static char *temp_install = NULL;
static char *top_builddir = NULL;
static FILE *logfile = NULL;

typedef struct _resultmap _resultmap;
struct _resultmap
{
	char *test;
	char *resultfile;
	_resultmap *next;
};
_resultmap *resultmap = NULL;
_stringlist *ignorelist = NULL;

/* copied from /backend/utils/mb/wchar.c */
static int
pg_ascii2wchar_with_len
(const unsigned char *from, pg_wchar *to, int len)
{
	int			cnt = 0;

	while (len > 0 && *from)
	{
		*to++ = *from++;
		len--;
		cnt++;
	}
	*to = 0;
	return cnt;
}


static void
load_resultmap(void)
{
	char buf[1024];
	pg_wchar host_platform_w[256];
	FILE *f;

	pg_ascii2wchar_with_len(host_platform, host_platform_w, (int)strlen(host_platform));

	sprintf(buf, "%s/resultmap", inputdir);
	f = fopen(buf,"r");
	if (!f)
	{
		fprintf(stderr,"Failed to load resultmap\n");
		exit(2);
	}
	memset(buf, 0, sizeof(buf));
	while (fgets(buf, sizeof(buf)-1, f))
	{
		char *platform;
		char *expected;
		char restr[256];
		pg_wchar restr_w[256];
		regex_t re;
		int r;

		if (buf[strlen(buf)-1] == '\n')
			buf[strlen(buf)-1] = '\0';

		platform = strchr(buf, '/');
		if (!platform)
		{
			fprintf(stderr,"Bad format of line in resultmap: '%s'\n", buf);
			exit(2);
		}
		*platform = '\0';
		platform++;
		expected = strrchr(platform, '=');
		if (!expected)
		{
			fprintf(stderr,"Bad format of line in resultmap: '%s'\n", buf);
			exit(2);
		}
		*expected = '\0';
		expected++;

		sprintf(restr, "^%s", platform);

		pg_ascii2wchar_with_len(restr, restr_w, (int)strlen(restr));
		if (pg_regcomp(&re, restr_w, pg_wchar_strlen(restr_w), REG_BASIC) != REG_OKAY) 
		{
			fprintf(stderr,"Failed to compile regexp: '%s'\n", restr);
			exit(2);
		}

		r = pg_regexec(&re, host_platform_w, pg_wchar_strlen(host_platform_w), 0, NULL, 0, NULL, 0);
		if (r == REG_OKAY)
		{
			_resultmap *entry = calloc(1, sizeof(_resultmap));
			entry->test = strdup(buf);
			entry->resultfile = strdup(expected);
			entry->next = resultmap;
			resultmap = entry;
		}
		else if (r != REG_NOMATCH)
		{
			fprintf(stderr,"Failed to match regexp: '%s'\n", restr);
			exit(2);
		}
	}
	fclose(f);
}

static void
doputenv(char *var, char *val)
{
	char *s = malloc(strlen(var)+strlen(val)+2);
	sprintf(s,"%s=%s", var, val);
	putenv(s);
}

static void
add_to_path(char *pathname, char separator, char *addval)
{
	char *oldval = getenv(pathname);
	char *newval;

	if (!oldval || !oldval[0]) 
	{
		/* no previous value */
		newval = malloc(strlen(pathname) + strlen(addval) + 2);
		sprintf(newval,"%s=%s",pathname,addval);
	}
	else
	{
		newval = malloc(strlen(pathname) + strlen(oldval) + strlen(addval) + 4);
		sprintf(newval,"%s=%s%c%s",pathname,addval,separator,oldval);
	}
	putenv(newval);
}

static void
initialize_environment(void)
{
	unsetenv("LC_COLLATE");
	unsetenv("LC_CTYPE");
	unsetenv("LC_MONETARY");
	unsetenv("LC_MESSAGES");
	unsetenv("LC_NUMERIC");
	unsetenv("LC_TIME");
	unsetenv("LC_ALL");
	unsetenv("LANG");
	unsetenv("LANGUAGE");
#if defined(WIN32) || defined(CYGWIN)
	putenv("LANG=en");
#endif

	putenv("PGTZ=PST8PDT");
	putenv("PGDATESTYLE=ISO, MDY");

	unsetenv("PGDATABASE");
	unsetenv("PGUSER");
	unsetenv("PGSERVICE");
	unsetenv("PGSSLMODE");
	unsetenv("PGREQUIRESSL");
	unsetenv("PGCONNECT_TIMEOUT");

	if (temp_install)
	{
		/* setup bin and lib dirs for temp install */
		bindir = malloc(strlen(temp_install) + 32);
		sprintf(bindir,"%s/install/usr/local/pgsql/bin/", temp_install);
		libdir = malloc(strlen(temp_install) + 32);
		sprintf(libdir,"%s/install/usr/local/pgsql/lib", temp_install);


		add_to_path("LD_LIBRARY_PATH", ':', libdir);
		add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
#ifdef WIN32
		add_to_path("PATH", ';', libdir);
#endif
	}

	if (port != -1)
	{
		char s[16];
		sprintf(s,"%i",port);
		doputenv("PGPORT",s);
	}
	if (hostname != NULL)
		doputenv("PGHOST", hostname);
	if (user != NULL)
		doputenv("PGUSER", user);

	load_resultmap();
}

static void
psql_command(char *database, char *query, ...)
{
	char query2[2048];
	char psql_cmd[2048];
	int r;
	va_list args;
	FILE *p;

	va_start(args, query);
	vsprintf(query2, query, args);
	va_end(args);

	sprintf(psql_cmd, "%spsql -X -c \"%s\" %s", bindir, query2, database);

	p = popen(psql_cmd, "r");
	if (!p)
	{
		fprintf(stderr, "Command failed: '%s'\n", psql_cmd);
		exit(2);
	}
	while (fgets(psql_cmd, sizeof(psql_cmd)-1, p))
	{
		fputs(psql_cmd, stdout);
	}
	r = pclose(p);
	if (r != 0)
	{
		fprintf(stderr," * Error *\n");
		exit(2);
	}
}

static PID_TYPE
spawn_process(char *cmdline)
{
#ifndef WIN32
	pid_t pid;

	pid = fork();
	if (pid == -1)
	{
		perror("unable to fork");
		exit(2);
	}
	if (pid == 0)
	{
		/* In child */
		exit(system(cmdline));
	}
	/* in parent */
	return pid;
#else
	char *cmdline2;
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);

	cmdline2 = malloc(strlen(cmdline) + 8);
	sprintf(cmdline2, "cmd /c %s", cmdline);

	if (!CreateProcess(NULL, cmdline2, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
	{
		fprintf(stderr,"Failed to start process for '%s': %lu\n", cmdline2, GetLastError());
		free(cmdline2);
		return INVALID_PID;
	}
	free(cmdline2);
	
	CloseHandle(pi.hThread);
	return pi.hProcess;
#endif
}

/* start a psql test process for specified file (including redirection), and return
 * an identifier that can be waited on */
static PID_TYPE
psql_start_test(char *basefile)
{
	char infile[1024];
	char outfile[1024];
	char psql_cmd[2048];

	sprintf(infile, "%s/sql/%s.sql", inputdir, basefile);
	sprintf(outfile, "%s/results/%s.out", outputdir, basefile);

	sprintf(psql_cmd, "%spsql -X -a -q -d %s <%s >%s 2>&1",
		bindir, dbname, infile, outfile);

	return spawn_process(psql_cmd);
}

static int
file_size(char *file)
{
	int r;
	FILE *f = fopen(file,"r");
	if (!f)
	{
		fprintf(stderr,"unable to open file '%s': %i", file, errno);
		return -1;
	}
	fseek(f, 0, SEEK_END);
	r = ftell(f);
	fclose(f);
	return r;
}

static int
file_line_count(char *file)
{
	int c;
	int l = 0;
	FILE *f = fopen(file,"r");
	if (!f)
	{
		fprintf(stderr,"unable to open file '%s': %i", file, errno);
		return -1;
	}
	while ((c = fgetc(f)) != EOF)
	{
		if (c == '\n')
			l++;
	}
	fclose(f);
	return l;
}

static bool
file_exists(char *file)
{
	FILE *f = fopen(file, "r");
	if (!f)
		return false;
	fclose(f);
	return true;
}

static bool
directory_exists(char *dir)
{
	struct stat st;
	if (stat(dir, &st)!= 0)
		return false;
	if (st.st_mode & S_IFDIR)
		return true;
	return false;
}

static bool
files_differ(char *basename)
{
	char cmd[1024];
	char diff[1024];
	int r;
	char expectfile[256];
	int last_line_count;
	char last_expect_file[256];
	_resultmap *rm;
	int i;

	sprintf(diff, "%s/%s.diff", outputdir, basename);
	sprintf(expectfile, "%s/expected/%s.out", inputdir, basename);

	/* Check in resultmap if we should be looking at a different file */
	for (rm = resultmap; rm != NULL; rm = rm->next)
	{
		if (!strcmp(basename, rm->test))
		{
			sprintf(expectfile, "%s/expected/%s.out", inputdir, rm->resultfile);
			break;
		}
	}

	sprintf(cmd, "diff -w %s %s/results/%s.out > %s", expectfile, outputdir, basename, diff);
	r = system(cmd);
	/* Don't care about return value, just read output file */

	/* Is the diff file there? */
	if (file_size(diff) == 0)
	{
		/* No diff = no changes = good */
		unlink(diff);
		return false;
	}
	last_line_count = file_line_count(diff);
	strcpy(last_expect_file, expectfile);

	/* diff exists, but there may be secondary output files that match better */
	for (i = 1; i < 10; i++)
	{
		sprintf(expectfile, "%s/expected/%s_%i.out", inputdir, basename, i);
		if (file_exists(expectfile))
		{
			int l;

			sprintf(cmd, "diff -w %s %s/results/%s.out > %s", expectfile, outputdir, basename, diff);
			r = system(cmd);

			if (file_size(diff) == 0)
			{
				/* No diff = no changes = good */
				unlink(diff);
				return false;
			}

			l = file_line_count(diff);
			if (l < last_line_count)
			{
				/* This diff was a better match than the last one */
				last_line_count = l;
				strcpy(last_expect_file, expectfile);
			}
		}
	}

	/* re-run the diff to get the smallest file back */
	sprintf(cmd, "diff -w %s/expected/%s %s/results/%s.out > %s", inputdir, last_expect_file, outputdir, basename, diff);

	return true;
}

static bool
run_schedule(void)
{
	char scbuf[1024];
	int success_count = 0;
	int fail_count = 0;
	int fail_ignore_count = 0;
	FILE *scf;
#ifndef WIN32
	int tests_left;
#endif

	sprintf(scbuf,"%s/results",outputdir);
	if (!directory_exists(scbuf))
		mkdir(scbuf,S_IRWXU);

	scf = fopen(schedule, "r");
	if (!scf)
	{
		fprintf(stderr,"unable to open schedule '%s'\n", schedule);
		exit(2);
	}

	memset(scbuf, 0, sizeof(scbuf));
	while (fgets(scbuf, sizeof(scbuf)-1, scf))
	{
		char *test;
		int num_tests = 1;
		char *c;
		PID_TYPE *pids;
		int i;
#ifdef WIN32
		int r;
#endif

		if (scbuf[0] == '\0' || scbuf[0] == '#')
			continue;
		if (scbuf[strlen(scbuf)-1] == '\n')
			scbuf[strlen(scbuf)-1] = '\0';
		if (scbuf[0] == '\0')
			continue;
		if (!strncmp(scbuf, "test: ", 6))
			test = scbuf + 6;
		else if (!strncmp(scbuf, "ignore: ", 8))
		{
			_stringlist *entry = calloc(1, sizeof(_stringlist));
			entry->str = strdup(scbuf + 8);
			entry->next = ignorelist;
			ignorelist = entry;
			continue;
		}
		else
		{
			fprintf(stderr,"unknown row in schedule file '%s': '%s'\n", schedule, scbuf);
			exit(2);
		}
		
		for (c = test; *c != '\0'; c++)
		{
			if (*c == ' ')
			{
				num_tests++;
				*c = '\0';
			}
		}

		pids = calloc(num_tests, sizeof(PID_TYPE));

		if (num_tests == 1)
		{
			status("test %-20s ...", test);
			pids[0] = psql_start_test(test);
		}
		else
		{
			status("parallel group (%i tests): ", num_tests);
			c = test;
			for (i = 0; i < num_tests; i++)
			{
				status("%s ", c);
				pids[i] = psql_start_test(c);
				if (pids[i] == INVALID_PID)
				{
					fprintf(stderr,"Failed to start process for test %s\n", c);
					exit(2);
				}
				c += strlen(c) + 1;
			}
		}

		/* Wait for tests to finish */
#ifndef WIN32
		tests_left = num_tests;
		while (tests_left > 0)
		{
			pid_t p = wait(NULL);
			if (p == -1)
			{
				fprintf(stderr,"Failed to wait(): %i\n", errno);
				exit(2);
			}
			for (i=0; i < num_tests; i++)
			{
				/* Make sure we only count the processes we explicitly started */
				if (p == pids[i])
				{
					pids[i] = -1;
					tests_left--;
				}
			}
		}
#else
		r = WaitForMultipleObjects(num_tests, pids, TRUE, INFINITE);
		if (r != WAIT_OBJECT_0)
		{
			fprintf(stderr,"Failed to wait for commands to finish: %ul\n", GetLastError());
			exit(2);
		}
		for (i = 0; i < num_tests; i++)
			CloseHandle(pids[i]);
#endif
		free(pids);

		/* Determine diffs for all tests */
		if (num_tests > 1)
			status_end(); /* Newline before we list all tests */
		c = test;
		for (i = 0; i < num_tests; i++)
		{
			if (num_tests > 1)
				status("     %-20s ...", c);

			if (files_differ(c))
			{
				bool ignore = false;
				_stringlist *sl;

				for (sl = ignorelist; sl != NULL; sl = sl->next)
				{
					if (!strcmp(c, sl->str))
					{
						ignore = true;
						break;
					}
				}
				if (ignore)
				{
					status(" failed (ignored)");
					fail_ignore_count++;
				}
				else
				{
					status(" FAILED");
					fail_count++;
				}
			}
			else
			{
				status(" ok");
				success_count++;
			}
			status_end();

			c += strlen(c) + 1;
		}
	}
	
	fclose(scf);
	if (temp_install)
	{
		sprintf(scbuf, "\"%spg_ctl\" -D \"%s/data\" stop", bindir, temp_install);
		system(scbuf);
	}

	printf("==============\n");
	if (fail_count == 0 && fail_ignore_count == 0)
		printf("All %i tests passed.\n", success_count);
	else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */
		printf("%i of %i tests passed, %i failed test(s) ignored.", success_count, success_count + fail_ignore_count, fail_ignore_count);
	else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */
		printf("%i of %i tests failed.\n", fail_count, success_count+fail_count+fail_ignore_count);
	else /* fail_count>0 && fail_ignore_count>0 */
		printf("%i of %i tests failed, %i of these failures ignored.",fail_count+fail_ignore_count, fail_count+fail_ignore_count+success_count, fail_ignore_count);
	printf("==============\n");

	return (fail_count == 0);
}

static void
open_logfile(void)
{
	char fn[1024];
	sprintf(fn,"%s/regression.out",outputdir);
	logfile = fopen(fn, "w");
	if (!logfile)
	{
		fprintf(stderr,"could not open %s\n", fn);
		exit(2);
	}
}

static void
help(char *self)
{
	printf("PostgreSQL regression test driver\n\
\n\
Usage: %s [options...] [extra tests...]\n\
\n\
Options:\n\
  --platform=PLATFORM       platform string for current machine, for example\n\
                            `i686-pc-linux-gnu'\n\
  --dbname=DB               use database DB (default `regression')\n\
  --debug                   turn on debug mode in programs that are run\n\
  --inputdir=DIR            take input files from DIR (default `.')\n\
  --load-language=lang      load the named language before running the\n\
                            tests; can appear multiple times\n\
  --multibyte=ENCODING      use ENCODING as the multibyte encoding, and\n\
                            also run a test by the same name\n\
  --outputdir=DIR           place output files in DIR (default `.')\n\
  --schedule=FILE           use test ordering schedule from FILE\n\
                            (may be used multiple times to concatenate)\n\
  --temp-install[=DIR]      create a temporary installation (in DIR)\n\
  --no-locale               use C locale\n\
\n\
Options for `temp-install' mode:\n\
  --top-builddir=DIR        (relative) path to top level build directory\n\
  --temp-port=PORT          port number to start temp postmaster on\n\
\n\
Options for using an existing installation:\n\
  --host=HOST               use postmaster running on HOST\n\
  --port=PORT               use postmaster running at PORT\n\
  --user=USER               connect as USER\n\
\n\
The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n\
if the tests could not be run for some reason.\n\
\n\
Report bugs to <pgsql-bugs@postgresql.org>.\n\n",
		self);
}

int
main(int argc, char *argv[])
{
	_stringlist *sl;
	int c;
	int option_index;
	bool result;
	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},
		{"multibyte", required_argument, NULL, 6},
		{"outputdir", required_argument, NULL, 7},
		{"schedule", required_argument, NULL, 8},
		{"temp-install", required_argument, NULL, 9},
		{"no-locale", no_argument, NULL, 10},
		{"top-builddir", required_argument, NULL, 11},
		{"temp-port", required_argument, NULL, 12},
		{"host", required_argument, NULL, 13},
		{"port", required_argument, NULL, 14},
		{"user", required_argument, NULL, 15},
		{"platform", required_argument, NULL, 16},
		{"bindir", required_argument, NULL, 17},
		{NULL, 0, NULL, 0}
	};

#if defined(WIN32) || defined(CYGWIN)
	hostname = "localhost"; /* no unix domain sockets available, so change default */
#endif

	while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
	{
		switch (c)
		{
			case 'h':
				help(argv[0]);
				exit(2);
			case 'V':
				printf("pg_regress (%s)", PG_VERSION_STR);
				exit(2);
			case 1:
				dbname = strdup(optarg);
				break;
			case 2:
				debug = true;
				break;
			case 3:
				inputdir = strdup(optarg);
				break;
			case 4:
				{
					_stringlist *entry = calloc(1, sizeof(_stringlist));
					entry->str = strdup(optarg);
					entry->next = loadlanguage;
					loadlanguage = entry;
				}
				break;
			case 6:
				encoding = strdup(optarg);
				break;
			case 7:
				outputdir = strdup(optarg);
				break;
			case 8:
				schedule = strdup(optarg);
				break;
			case 9:
				if (optarg[0] == '/')
					temp_install = strdup(optarg);
				else
				{
					char cwdbuf[1024];
					/* temp_install must be rooted */
					if (!getcwd(cwdbuf,sizeof(cwdbuf)))
					{
						fprintf(stderr,"unable to get current directory: %i\n", errno);
						exit(2);
					}
					temp_install = malloc(strlen(optarg) + strlen(cwdbuf) + 2);
					sprintf(temp_install,"%s/%s", cwdbuf, optarg);
				}
				break;
			case 10:
				nolocale = true;
				break;
			case 11:
				top_builddir = strdup(optarg);
				break;
			case 12:
				tempport = atoi(optarg);
				break;
			case 13:
				hostname = strdup(optarg);
				break;
			case 14:
				port = atoi(optarg);
				break;
			case 15:
				user = strdup(optarg);
				break;
			case 16:
				host_platform = strdup(optarg);
				break;
			case 17:
				bindir = strdup(optarg);
				break;
			default:
				/* getopt_long already emitted a complaint */
				fprintf(stderr,"\nTry \"%s -h\" for more information.\n",argv[0]);
				exit(2);
		}
	}

	if (!host_platform)
	{
		fprintf(stderr,"platform must be specified\n");
		exit(2);
	}

	if (temp_install)
		port = tempport;

	open_logfile();

	initialize_environment();

	if (temp_install)
	{
		char buf[1024];
		int i;

		if (!top_builddir)
		{
			fprintf(stderr,"--top-builddir must be specified when using temp-install\n");
			exit(2);
		}
		
		if (directory_exists(temp_install))
		{
			header("removing existing temp installation");
			rmtree(temp_install,true);
		}
		mkdir(temp_install,S_IRWXU);
		sprintf(buf,"%s/log",temp_install);
		mkdir(buf,S_IRWXU);

		header("creating temporary installation");
		sprintf(buf,"make -C \"%s\" DESTDIR=\"%s/install\" install with_perl=no with_python=no > \"%s/log/install.log\" 2>&1",
			top_builddir, temp_install, outputdir);
		if (WEXITSTATUS(system(buf)) != 0)
		{
			fprintf(stderr, "installation failed.\nExamine %s/log/install.log for the reason.\n", outputdir);
			exit(2);
		}

		header("initializing database system");
		sprintf(buf,"\"%sinitdb\" -D \"%s/data\" -L \"%s/install/usr/local/pgsql/share\" --noclean %s %s > %s/log/initdb.log 2>&1",
			bindir, temp_install, temp_install, nolocale?"--no-locale":"", debug?"--debug":"", outputdir);
		if (WEXITSTATUS(system(buf)) != 0)
		{
			fprintf(stderr,"initdb failed.\nExamine %s/log/initdb.log for the reason.\n", outputdir);
			exit(2);
		}

		header("starting postmaster");
		sprintf(buf,"\"%spostmaster\" -D \"%s/data\" -F %s -c listen_addresses=%s >\"%s/log/postmaster.log\" 2>&1",
			bindir, temp_install, debug?"-d5":"", hostname?hostname:"", outputdir);
		if (spawn_process(buf) == INVALID_PID)
		{
			fprintf(stderr,"postmaster failed.\nExamine %s/log/postmaster.log for the reason\n", outputdir);
			exit(2);
		}
		
		sprintf(buf,"\"%spsql\" -X -c \"SELECT 1;\" postgres >%s 2>&1",
			bindir, DEVNULL);
		for (i = 0; i < 60; i++)
		{
			int r = system(buf);
			if (r == 0)
				break;
			pg_usleep(1000000);
		}
		if (i == 60)
		{
			fprintf(stderr,"postmaster did not start in 60 seconds. Verify running processes.\n");
			exit(2);
		}
	}
	else
	{
		header("dropping database \"%s\"", dbname);
		psql_command("postgres","DROP DATABASE IF EXISTS %s", dbname);
	}

	header("creating database \"%s\"", dbname);
	psql_command("postgres","CREATE DATABASE regression TEMPLATE=template0 ENCODING='%s'", encoding);
	psql_command("postgres","ALTER DATABASE regression SET lc_messages TO 'C';ALTER DATABASE regression SET lc_monetary TO 'C';ALTER DATABASE regression SET lc_numeric TO 'C';ALTER DATABASE regression SET lc_time TO 'C';");

	header("dropping regression test user accounts");
	psql_command(dbname,"DROP GROUP IF EXISTS regressgroup1; DROP GROUP IF EXISTS regressgroup2; DROP USER IF EXISTS regressuser1, regressuser2, regressuser3, regressuser4;");

	for (sl = loadlanguage; sl != NULL; sl = sl->next)
	{
		header("installing %s", sl->str);
		psql_command(dbname, "CREATE LANGUAGE %s", sl->str);
	}

	// Set datestyle for horology tests
	putenv("PGDATESTYLE=Postgres, MDY");

	header("running regression test queries...");
	result = run_schedule();

	fclose(logfile);

	if (!result)
		exit(1);

	return 0;
}
