text: /*-------------------------------------------------------------------------
   *
   * pg_regress --- regression test driver
   *
   * This is a C implementation of the previous shell script for running
   * the regression tests, and should be mostly compatible with it.
   * Initial author of C translation: Magnus Hagander
   *
   * This code is released under the terms of the PostgreSQL License.
   *
   * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   * src/test/regress/pg_regress.c
   *
   *-------------------------------------------------------------------------
   */
  
  #include "postgres_fe.h"
  
  #include <ctype.h>
  #include <sys/stat.h>
  #include <sys/wait.h>
  #include <signal.h>
  #include <unistd.h>
  
  #ifdef HAVE_SYS_RESOURCE_H
  #include <sys/time.h>
  #include <sys/resource.h>
  #endif
  
  #include "pg_regress.h"
  
  #include "common/restricted_token.h"
  #include "common/username.h"
  #include "getopt_long.h"
  #include "libpq/pqcomm.h"		/* needed for UNIXSOCK_PATH() */
  #include "pg_config_paths.h"
  
  /* for resultmap we need a list of pairs of strings */
  typedef struct _resultmap
  {
  	char	   *test;
  	char	   *type;
  	char	   *resultfile;
  	struct _resultmap *next;
  } _resultmap;
  
  /*
   * Values obtained from Makefile.
   */
  char	   *host_platform = HOST_TUPLE;
  
  #ifndef WIN32					/* not used in WIN32 case */
  static char *shellprog = SHELLPROG;
  #endif
  
  /*
   * On Windows we use -w in diff switches to avoid problems with inconsistent
   * newline representation.  The actual result files will generally have
   * Windows-style newlines, but the comparison files might or might not.
   */
  #ifndef WIN32
  const char *basic_diff_opts = "";
  const char *pretty_diff_opts = "-C3";
  #else
  const char *basic_diff_opts = "-w";
  const char *pretty_diff_opts = "-w -C3";
  #endif
  
  /* options settable from command line */
  _stringlist *dblist = NULL;
  bool		debug = false;
  char	   *inputdir = ".";
  char	   *outputdir = ".";
  char	   *bindir = PGBINDIR;
  char	   *launcher = NULL;
  static _stringlist *loadlanguage = NULL;
  static _stringlist *loadextension = NULL;
  static int	max_connections = 0;
  static int	max_concurrent_tests = 0;
  static char *encoding = NULL;
  static _stringlist *schedulelist = NULL;
  static _stringlist *extra_tests = NULL;
  static char *temp_instance = NULL;
  static _stringlist *temp_configs = NULL;
  static bool nolocale = false;
  static bool use_existing = false;
  static char *hostname = NULL;
  static int	port = -1;
  static bool port_specified_by_user = false;
  static char *dlpath = PKGLIBDIR;
  static char *user = NULL;
  static _stringlist *extraroles = NULL;
  static char *config_auth_datadir = NULL;
  
  /* internal variables */
  static const char *progname;
  static char *logfilename;
  static FILE *logfile;
  static char *difffilename;
  static const char *sockdir;
  #ifdef HAVE_UNIX_SOCKETS
  static const char *temp_sockdir;
  static char sockself[MAXPGPATH];
  static char socklock[MAXPGPATH];
  #endif
  
  static _resultmap *resultmap = NULL;
  
  static PID_TYPE postmaster_pid = INVALID_PID;
  static bool postmaster_running = false;
  
  static int	success_count = 0;
  static int	fail_count = 0;
  static int	fail_ignore_count = 0;
  
  /* POLAR */
  static int 	ignore_count = 0;
  static int	polar_ignore_count = 0;
  /* POLAR end */
  
  static bool directory_exists(const char *dir);
  static void make_directory(const char *dir);
  
  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);
  
  /* POLAR */
  bool polar_is_enable = true;
  
  /*
   * allow core files if possible.
   */
  #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
  static void
  unlimit_core_size(void)
  {
  	struct rlimit lim;
  
  	getrlimit(RLIMIT_CORE, &lim);
  	if (lim.rlim_max == 0)
  	{
  		fprintf(stderr,
  				_("%s: could not set core size: disallowed by hard limit\n"),
  				progname);
  		return;
  	}
  	else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
  	{
  		lim.rlim_cur = lim.rlim_max;
  		setrlimit(RLIMIT_CORE, &lim);
  	}
  }
  #endif
  
  
  /*
   * Add an item at the end of a stringlist.
   */
  void
  add_stringlist_item(_stringlist **listhead, const char *str)
  {
  	_stringlist *newentry = pg_malloc(sizeof(_stringlist));
  	_stringlist *oldentry;
  
  	newentry->str = pg_strdup(str);
  	newentry->next = NULL;
  	if (*listhead == NULL)
  		*listhead = newentry;
  	else
  	{
  		for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
  			 /* skip */ ;
  		oldentry->next = newentry;
  	}
  }
  
  /*
   * Free a stringlist.
   */
  static void
  free_stringlist(_stringlist **listhead)
  {
  	if (listhead == NULL || *listhead == NULL)
  		return;
  	if ((*listhead)->next != NULL)
  		free_stringlist(&((*listhead)->next));
  	free((*listhead)->str);
  	free(*listhead);
  	*listhead = NULL;
  }
  
  /*
   * Split a delimited string into a stringlist
   */
  static void
  split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
  {
  	char	   *sc = pg_strdup(s);
  	char	   *token = strtok(sc, delim);
  
  	while (token)
  	{
  		add_stringlist_item(listhead, token);
  		token = strtok(NULL, delim);
  	}
  	free(sc);
  }
  
  /*
   * Print a progress banner on stdout.
   */
  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 */
  		char		buf[MAXPGPATH * 2];
  		int			r;
  
  		/* On Windows, system() seems not to force fflush, so... */
  		fflush(stdout);
  		fflush(stderr);
  
  		snprintf(buf, sizeof(buf),
  				 "\"%s%spg_ctl\" stop -D \"%s/data\" -s",
  				 bindir ? bindir : "",
  				 bindir ? "/" : "",
  				 temp_instance);
  		r = system(buf);
  		if (r != 0)
  		{
  			fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
  					progname, r);
  			_exit(2);			/* not exit(), that could be recursive */
  		}
  
  		postmaster_running = false;
  	}
  }
  
  #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)
  {
  	char	   *template = pg_strdup("/tmp/pg_regress-XXXXXX");
  
  	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;
  }
  #endif							/* HAVE_UNIX_SOCKETS */
  
  /*
   * 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;
  }
  
  /*
   * Replace all occurrences of a string in a string with a different string.
   * NOTE: Assumes there is enough room in the target buffer!
   */
  void
  replace_string(char *string, const char *replace, const char *replacement)
  {
  	char	   *ptr;
  
  	while ((ptr = strstr(string, replace)) != NULL)
  	{
  		char	   *dup = pg_strdup(string);
  
  		strlcpy(string, dup, ptr - string + 1);
  		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
  convert_sourcefiles_in(const char *source_subdir, const char *dest_dir, const char *dest_subdir, const char *suffix)
  {
  	char		testtablespace[MAXPGPATH];
  	char		indir[MAXPGPATH];
  	struct stat st;
  	int			ret;
  	char	  **name;
  	char	  **names;
  	int			count = 0;
  
  	snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir);
  
  	/* Check that indir actually exists and is a directory */
  	ret = stat(indir, &st);
  	if (ret != 0 || !S_ISDIR(st.st_mode))
  	{
  		/*
  		 * No warning, to avoid noise in tests that do not have these
  		 * directories; for example, ecpg, contrib and src/pl.
  		 */
  		return;
  	}
  
  	names = pgfnames(indir);
  	if (!names)
  		/* Error logged in pgfnames */
  		exit(2);
  
  	snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
  
  #ifdef WIN32
  
  	/*
  	 * On Windows only, clean out the test tablespace dir, or create it if it
  	 * doesn't exist.  On other platforms we expect the Makefile to take care
  	 * of that.  (We don't migrate that functionality in here because it'd be
  	 * harder to cope with platform-specific issues such as SELinux.)
  	 *
  	 * XXX it would be better if pg_regress.c had nothing at all to do with
  	 * testtablespace, and this were handled by a .BAT file or similar on
  	 * Windows.  See pgsql-hackers discussion of 2008-01-18.
  	 */
  	if (directory_exists(testtablespace))
  		if (!rmtree(testtablespace, true))
  		{
  			fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\"\n"),
  					progname, testtablespace);
  			exit(2);
  		}
  	make_directory(testtablespace);
  #endif
  
  	/* finally loop on each file and do the replacement */
  	for (name = names; *name; name++)
  	{
  		char		srcfile[MAXPGPATH];
  		char		destfile[MAXPGPATH];
  		char		prefix[MAXPGPATH];
  		FILE	   *infile,
  				   *outfile;
  		char		line[1024];
  
  		/* 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);
  		snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir,
  				 prefix, suffix);
  
  		infile = fopen(srcfile, "r");
  		if (!infile)
  		{
  			fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
  					progname, srcfile, strerror(errno));
  			exit(2);
  		}
  		outfile = fopen(destfile, "w");
  		if (!outfile)
  		{
  			fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  					progname, destfile, strerror(errno));
  			exit(2);
  		}
  		while (fgets(line, sizeof(line), infile))
  		{
  			replace_string(line, "@abs_srcdir@", inputdir);
  			replace_string(line, "@abs_builddir@", outputdir);
  			replace_string(line, "@testtablespace@", testtablespace);
  			replace_string(line, "@libdir@", dlpath);
  			replace_string(line, "@DLSUFFIX@", DLSUFFIX);
  			fputs(line, outfile);
  		}
  		fclose(infile);
  		fclose(outfile);
  	}
  
  	/*
  	 * If we didn't process any files, complain because it probably means
  	 * somebody neglected to pass the needed --inputdir argument.
  	 */
  	if (count <= 0)
  	{
  		fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"),
  				progname, indir);
  		exit(2);
  	}
  
  	pgfnames_cleanup(names);
  }
  
  /* Create the .sql and .out files from the .source files, if any */
  static void
  convert_sourcefiles(void)
  {
  	convert_sourcefiles_in("input", outputdir, "sql", "sql");
  	convert_sourcefiles_in("output", outputdir, "expected", "out");
  }
  
  /*
   * Scan resultmap file to find which platform-specific expected files to use.
   *
   * The format of each line of the file is
   *		   testname/hostplatformpattern=substitutefile
   * 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
   * matched against is the config.guess output.  (In the shell-script version,
   * 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)
  {
  	char		buf[MAXPGPATH];
  	FILE	   *f;
  
  	/* scan the file ... */
  	snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
  	f = fopen(buf, "r");
  	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));
  		exit(2);
  	}
  
  	while (fgets(buf, sizeof(buf), f))
  	{
  		char	   *platform;
  		char	   *file_type;
  		char	   *expected;
  		int			i;
  
  		/* strip trailing whitespace, especially the newline */
  		i = strlen(buf);
  		while (i > 0 && isspace((unsigned char) buf[i - 1]))
  			buf[--i] = '\0';
  
  		/* parse out the line fields */
  		file_type = strchr(buf, ':');
  		if (!file_type)
  		{
  			fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
  					buf);
  			exit(2);
  		}
  		*file_type++ = '\0';
  
  		platform = strchr(file_type, ':');
  		if (!platform)
  		{
  			fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
  					buf);
  			exit(2);
  		}
  		*platform++ = '\0';
  		expected = strchr(platform, '=');
  		if (!expected)
  		{
  			fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
  					buf);
  			exit(2);
  		}
  		*expected++ = '\0';
  
  		/*
  		 * 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.
  		 */
  		if (string_matches_pattern(host_platform, platform))
  		{
  			_resultmap *entry = pg_malloc(sizeof(_resultmap));
  
  			entry->test = pg_strdup(buf);
  			entry->type = pg_strdup(file_type);
  			entry->resultfile = pg_strdup(expected);
  			entry->next = resultmap;
  			resultmap = entry;
  		}
  	}
  	fclose(f);
  }
  
  /*
   * Check in resultmap if we should be looking at a different file
   */
  static
  const char *
  get_expectfile(const char *testname, const char *file)
  {
  	char	   *file_type;
  	_resultmap *rm;
  
  	/*
  	 * Determine the file type from the file name. This is just what is
  	 * following the last dot in the file name.
  	 */
  	if (!file || !(file_type = strrchr(file, '.')))
  		return NULL;
  
  	file_type++;
  
  	for (rm = resultmap; rm != NULL; rm = rm->next)
  	{
  		if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
  		{
  			return rm->resultfile;
  		}
  	}
  
  	return NULL;
  }
  
  /*
   * Handy subroutine for setting an environment variable "var" to "val"
   */
  static void
  doputenv(const char *var, const char *val)
  {
  	char	   *s;
  
  	s = psprintf("%s=%s", var, val);
  	putenv(s);
  }
  
  /*
   * Prepare environment variables for running regression tests
   */
  static void
  initialize_environment(void)
  {
  	putenv("PGAPPNAME=pg_regress");
  
  	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");
  
  		/*
  		 * Most platforms have adopted the POSIX locale as their
  		 * implementation-defined default locale.  Exceptions include native
  		 * Windows, macOS with --enable-nls, and Cygwin with --enable-nls.
  		 * (Use of --enable-nls matters because libintl replaces setlocale().)
  		 * Also, PostgreSQL does not support macOS with locale environment
  		 * variables unset; see PostmasterMain().
  		 */
  #if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
  		putenv("LANG=C");
  #endif
  	}
  
  	/*
  	 * Set translation-related settings to English; otherwise psql will
  	 * produce translated messages and produce diffs.  (XXX If we ever support
  	 * translation of pg_regress, this needs to be moved elsewhere, where psql
  	 * is actually called.)
  	 */
  	unsetenv("LANGUAGE");
  	unsetenv("LC_ALL");
  	putenv("LC_MESSAGES=C");
  
  	/*
  	 * Set encoding as requested
  	 */
  	if (encoding)
  		doputenv("PGCLIENTENCODING", encoding);
  	else
  		unsetenv("PGCLIENTENCODING");
  
  	/*
  	 * Set timezone and datestyle for datetime-related tests
  	 */
  	putenv("PGTZ=PST8PDT");
  	putenv("PGDATESTYLE=Postgres, MDY");
  
  	/*
  	 * Likewise set intervalstyle to ensure consistent results.  This is a bit
  	 * more painful because we must use PGOPTIONS, and we want to preserve the
  	 * user's ability to set other variables through that.
  	 */
  	{
  		const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
  		const char *old_pgoptions = getenv("PGOPTIONS");
  		char	   *new_pgoptions;
  
  		if (!old_pgoptions)
  			old_pgoptions = "";
  		new_pgoptions = psprintf("PGOPTIONS=%s %s",
  								 old_pgoptions, my_pgoptions);
  		putenv(new_pgoptions);
  	}
  
  	if (temp_instance)
  	{
  		/*
  		 * 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
  		 * PGHOST depending on whether we are using TCP or Unix sockets.
  		 */
  		unsetenv("PGDATABASE");
  		unsetenv("PGUSER");
  		unsetenv("PGSERVICE");
  		unsetenv("PGSSLMODE");
  		unsetenv("PGREQUIRESSL");
  		unsetenv("PGCONNECT_TIMEOUT");
  		unsetenv("PGDATA");
  #ifdef HAVE_UNIX_SOCKETS
  		if (hostname != NULL)
  			doputenv("PGHOST", hostname);
  		else
  		{
  			sockdir = getenv("PG_REGRESS_SOCK_DIR");
  			if (!sockdir)
  				sockdir = make_temp_sockdir();
  			doputenv("PGHOST", sockdir);
  		}
  #else
  		Assert(hostname != NULL);
  		doputenv("PGHOST", hostname);
  #endif
  		unsetenv("PGHOSTADDR");
  		if (port != -1)
  		{
  			char		s[16];
  
  			sprintf(s, "%d", port);
  			doputenv("PGPORT", s);
  		}
  	}
  	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)
  		{
  			char		s[16];
  
  			sprintf(s, "%d", port);
  			doputenv("PGPORT", s);
  		}
  		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"));
  	}
  
  	convert_sourcefiles();
  	load_resultmap();
  }
  
  pg_attribute_unused()
  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;
  }
  
  #ifdef ENABLE_SSPI
  /*
   * 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,
  				_("%s: could not get token information buffer size: error code %lu\n"),
  				progname, GetLastError());
  		exit(2);
  	}
  	tokenuser = pg_malloc(retlen);
  	if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
  	{
  		fprintf(stderr,
  				_("%s: could not get token information: error code %lu\n"),
  				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;
  	bool		have_ipv6;
  	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);
  	}
  
  	/*
  	 * 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);
  	}
  
  	/* 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);
  	if (res < 0 || res >= sizeof(fname) - 1)
  	{
  		/*
  		 * 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);
  	if (have_ipv6)
  		CW(fputs("host all all ::1/128  sspi include_realm=1 map=regress\n",
  				 hba) >= 0);
  	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.
  	 */
  	CW(fprintf(ident, "regress  \"%s@%s\"  %s\n",
  			   accountname, domainname, fmtHba(username)) >= 0);
  	for (sl = extraroles; sl; sl = sl->next)
  		CW(fprintf(ident, "regress  \"%s@%s\"  %s\n",
  				   accountname, domainname, fmtHba(sl->str)) >= 0);
  	CW(fclose(ident) == 0);
  }
  #endif
  
  /*
   * Issue a command via psql, connecting to the specified database
   *
   * Since we use system(), this doesn't return until the operation finishes
   */
  static void
  psql_command(const char *database, const char *query,...)
  {
  	char		query_formatted[1024];
  	char		query_escaped[2048];
  	char		psql_cmd[MAXPGPATH + 2048];
  	va_list		args;
  	char	   *s;
  	char	   *d;
  
  	/* 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),
  			 "\"%s%spsql\" -X -c \"%s\" \"%s\"",
  			 bindir ? bindir : "",
  			 bindir ? "/" : "",
  			 query_escaped,
  			 database);
  
  	if (system(psql_cmd) != 0)
  	{
  		/* psql probably already reported the error */
  		fprintf(stderr, _("command failed: %s\n"), psql_cmd);
  		exit(2);
  	}
  }
  
  /*
   * Spawn a process to execute the given shell command; don't wait for it
   *
   * Returns the process ID (or HANDLE) so we can wait for it later
   */
  PID_TYPE
  spawn_process(const char *cmdline)
  {
  #ifndef WIN32
  	pid_t		pid;
  
  	/*
  	 * Must flush I/O buffers before fork.  Ideally we'd use fflush(NULL) here
  	 * ... 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));
  		exit(2);
  	}
  	if (pid == 0)
  	{
  		/*
  		 * In child
  		 *
  		 * Instead of using system(), exec the shell directly, and tell it to
  		 * "exec" the command too.  This saves two useless processes per
  		 * parallel test case.
  		 */
  		char	   *cmdline2;
  
  		cmdline2 = psprintf("exec %s", cmdline);
  		execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
  		fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
  				progname, shellprog, strerror(errno));
  		_exit(1);				/* not exit() here... */
  	}
  	/* in parent */
  	return pid;
  #else
  	PROCESS_INFORMATION pi;
  	char	   *cmdline2;
  	HANDLE		restrictedToken;
  
  	memset(&pi, 0, sizeof(pi));
  	cmdline2 = psprintf("cmd /c \"%s\"", cmdline);
  
  	if ((restrictedToken =
  		 CreateRestrictedProcess(cmdline2, &pi, progname)) == 0)
  		exit(2);
  
  	CloseHandle(pi.hThread);
  	return pi.hProcess;
  #endif
  }
  
  /*
   * Count bytes in file
   */
  static long
  file_size(const char *file)
  {
  	long		r;
  	FILE	   *f = fopen(file, "r");
  
  	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)
  {
  	int			c;
  	int			l = 0;
  	FILE	   *f = fopen(file, "r");
  
  	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;
  }
  
  bool
  file_exists(const char *file)
  {
  	FILE	   *f = fopen(file, "r");
  
  	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;
  	if (S_ISDIR(st.st_mode))
  		return true;
  	return false;
  }
  
  /* Create a directory */
  static void
  make_directory(const char *dir)
  {
  	if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
  	{
  		fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
  				progname, dir, strerror(errno));
  		exit(2);
  	}
  }
  
  /*
   * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
   */
  static char *
  get_alternative_expectfile(const char *expectfile, int i)
  {
  	char	   *last_dot;
  	int			ssize = strlen(expectfile) + 2 + 1;
  	char	   *tmp;
  	char	   *s;
  
  	if (!(tmp = (char *) malloc(ssize)))
  		return NULL;
  
  	if (!(s = (char *) malloc(ssize)))
  	{
  		free(tmp);
  		return NULL;
  	}
  
  	strcpy(tmp, expectfile);
  	last_dot = strrchr(tmp, '.');
  	if (!last_dot)
  	{
  		free(tmp);
  		free(s);
  		return NULL;
  	}
  	*last_dot = '\0';
  	snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
  	free(tmp);
  	return s;
  }
  
  /*
   * Run a "diff" command and also check that it didn't crash
   */
  static int
  run_diff(const char *cmd, const char *filename)
  {
  	int			r;
  
  	r = system(cmd);
  	if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
  	{
  		fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
  		exit(2);
  	}
  #ifdef WIN32
  
  	/*
  	 * On WIN32, if the 'diff' command cannot be found, system() returns 1,
  	 * but produces nothing to stdout, so we check for that here.
  	 */
  	if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
  	{
  		fprintf(stderr, _("diff command not found: %s\n"), cmd);
  		exit(2);
  	}
  #endif
  
  	return WEXITSTATUS(r);
  }
  
  /*
   * 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
  results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
  {
  	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;
  	const char *platform_expectfile;
  
  	/*
  	 * We can pass either the resultsfile or the expectfile, they should have
  	 * the same type (filename.type) anyway.
  	 */
  	platform_expectfile = get_expectfile(testname, resultsfile);
  
  	strlcpy(expectfile, default_expectfile, sizeof(expectfile));
  	if (platform_expectfile)
  	{
  		/*
  		 * Replace everything after the last slash in expectfile with what the
  		 * platform_expectfile contains.
  		 */
  		char	   *p = strrchr(expectfile, '/');
  
  		if (p)
  			strcpy(++p, platform_expectfile);
  	}
  
  	/* Name to use for temporary diff file */
  	snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
  
  	/* OK, run the diff */
  	snprintf(cmd, sizeof(cmd),
  			 "diff %s \"%s\" \"%s\" > \"%s\"",
  			 basic_diff_opts, expectfile, resultsfile, diff);
  
  	/* Is the diff file empty? */
  	if (run_diff(cmd, diff) == 0)
  	{
  		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++)
  	{
  		char	   *alt_expectfile;
  
  		alt_expectfile = get_alternative_expectfile(expectfile, i);
  		if (!alt_expectfile)
  		{
  			fprintf(stderr, _("Unable to check secondary comparison files: %s\n"),
  					strerror(errno));
  			exit(2);
  		}
  
  		if (!file_exists(alt_expectfile))
  		{
  			free(alt_expectfile);
  			continue;
  		}
  
  		snprintf(cmd, sizeof(cmd),
  				 "diff %s \"%s\" \"%s\" > \"%s\"",
  				 basic_diff_opts, alt_expectfile, resultsfile, diff);
  
  		if (run_diff(cmd, diff) == 0)
  		{
  			unlink(diff);
  			free(alt_expectfile);
  			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;
  			strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file));
  		}
  		free(alt_expectfile);
  	}
  
  	/*
  	 * fall back on the canonical results file if we haven't tried it yet and
  	 * haven't found a complete match yet.
  	 */
  
  	if (platform_expectfile)
  	{
  		snprintf(cmd, sizeof(cmd),
  				 "diff %s \"%s\" \"%s\" > \"%s\"",
  				 basic_diff_opts, default_expectfile, resultsfile, diff);
  
  		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;
  			strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file));
  		}
  	}
  
  	/*
  	 * Use the best comparison file to generate the "pretty" diff, which we
  	 * append to the diffs summary file.
  	 */
  	snprintf(cmd, sizeof(cmd),
  			 "diff %s \"%s\" \"%s\" >> \"%s\"",
  			 pretty_diff_opts, best_expect_file, resultsfile, difffilename);
  	run_diff(cmd, difffilename);
  
  	/* And append a separator */
  	difffile = fopen(difffilename, "a");
  	if (difffile)
  	{
  		fprintf(difffile,
  				"\n======================================================================\n\n");
  		fclose(difffile);
  	}
  
  	unlink(diff);
  	return true;
  }
  
  /*
   * Wait for specified subprocesses to finish, and return their exit
   * statuses into statuses[]
   *
   * If names isn't NULL, print each subprocess's name as it finishes
   *
   * Note: it's OK to scribble on the pids array, but not on the names array
   */
  static void
  wait_for_tests(PID_TYPE * pids, int *statuses, char **names, int num_tests)
  {
  	int			tests_left;
  	int			i;
  
  #ifdef WIN32
  	PID_TYPE   *active_pids = pg_malloc(num_tests * sizeof(PID_TYPE));
  
  	memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
  #endif
  
  	tests_left = num_tests;
  	while (tests_left > 0)
  	{
  		PID_TYPE	p;
  
  #ifndef WIN32
  		int			exit_status;
  
  		p = wait(&exit_status);
  
  		if (p == INVALID_PID)
  		{
  			fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
  					strerror(errno));
  			exit(2);
  		}
  #else
  		DWORD		exit_status;
  		int			r;
  
  		r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
  		if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
  		{
  			fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"),
  					GetLastError());
  			exit(2);
  		}
  		p = active_pids[r - WAIT_OBJECT_0];
  		/* compact the active_pids array */
  		active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
  #endif							/* WIN32 */
  
  		for (i = 0; i < num_tests; i++)
  		{
  			if (p == pids[i])
  			{
  #ifdef WIN32
  				GetExitCodeProcess(pids[i], &exit_status);
  				CloseHandle(pids[i]);
  #endif
  				pids[i] = INVALID_PID;
  				statuses[i] = (int) exit_status;
  				if (names)
  					status(" %s", names[i]);
  				tests_left--;
  				break;
  			}
  		}
  	}
  
  #ifdef WIN32
  	free(active_pids);
  #endif
  }
  
  /*
   * report nonzero exit code from a test process
   */
  static void
  log_child_failure(int exitstatus)
  {
  	if (WIFEXITED(exitstatus))
  		status(_(" (test process exited with exit code %d)"),
  			   WEXITSTATUS(exitstatus));
  	else if (WIFSIGNALED(exitstatus))
  	{
  #if defined(WIN32)
  		status(_(" (test process was terminated by exception 0x%X)"),
  			   WTERMSIG(exitstatus));
  #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
  		status(_(" (test process was terminated by signal %d: %s)"),
  			   WTERMSIG(exitstatus),
  			   WTERMSIG(exitstatus) < NSIG ?
  			   sys_siglist[WTERMSIG(exitstatus)] : "(unknown))");
  #else
  		status(_(" (test process was terminated by signal %d)"),
  			   WTERMSIG(exitstatus));
  #endif
  	}
  	else
  		status(_(" (test process exited with unrecognized status %d)"),
  			   exitstatus);
  }
  
  /*
   * Run all the tests specified in one schedule file
   */
  static void
  run_schedule(const char *schedule, test_function tfunc)
  {
  #define MAX_PARALLEL_TESTS 100
  	char	   *tests[MAX_PARALLEL_TESTS];
  	_stringlist *resultfiles[MAX_PARALLEL_TESTS];
  	_stringlist *expectfiles[MAX_PARALLEL_TESTS];
  	_stringlist *tags[MAX_PARALLEL_TESTS];
  	PID_TYPE	pids[MAX_PARALLEL_TESTS];
  	int			statuses[MAX_PARALLEL_TESTS];
  	_stringlist *ignorelist = NULL;
  	char		scbuf[1024];
  	FILE	   *scf;
  	int			line_num = 0;
  	/* POLAR */
  	_stringlist *polar_ignorelist = NULL;
  
  	memset(tests, 0, sizeof(tests));
  	memset(resultfiles, 0, sizeof(resultfiles));
  	memset(expectfiles, 0, sizeof(expectfiles));
  	memset(tags, 0, sizeof(tags));
  
  	scf = fopen(schedule, "r");
  	if (!scf)
  	{
  		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
  				progname, schedule, strerror(errno));
  		exit(2);
  	}
  
  	while (fgets(scbuf, sizeof(scbuf), scf))
  	{
  		char	   *test = NULL;
  		char	   *c;
  		int			num_tests;
  		bool		inword;
  		int			i;
  
  		line_num++;
  
  		/* strip trailing whitespace, especially the newline */
  		i = strlen(scbuf);
  		while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
  			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);
  
  			/*
  			 * Note: ignore: lines do not run the test, they just say that
  			 * 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.
  			 */
  			continue;
  		}
  		else if (strncmp(scbuf, "polar_ignore: ", 14) == 0)
  		{
  			/*
  			 * polar_ignore: some cases is ignored in master-slave sync
  			 * cases will run but will not output to the regression.diff
  			 * usage: the same with "ignore"
  			 * 1. polar_ignore: case
  			 *    test: case
  			 * run the case but no output in regression.diff
  			 * 2. polar_ignore: case
  			 * not run the case
  			 */
  			c = scbuf + 14;
  			while (*c && isspace((unsigned char) *c))
  				c++;
  			add_stringlist_item(&polar_ignorelist, c);
  			if (polar_is_enable)
  				polar_ignore_count ++;
  			continue;
  		}
  		else
  		{
  			fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
  					schedule, line_num, scbuf);
  			exit(2);
  		}
  
  		num_tests = 0;
  		inword = false;
  		for (c = test;; c++)
  		{
  			if (*c == '\0' || isspace((unsigned char) *c))
  			{
  				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 */
  			}
  			else if (!inword)
  			{
  				/* Start of a test name */
  				test = c;
  				inword = true;
  			}
  		}
  
  		if (num_tests == 0)
  		{
  			fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
  					schedule, line_num, scbuf);
  			exit(2);
  		}
  
  		if (num_tests == 1)
  		{
  			status(_("test %-28s ... "), tests[0]);
  			pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
  			wait_for_tests(pids, statuses, NULL, 1);
  			/* status line is finished below */
  		}
  		else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests)
  		{
  			fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
  					max_concurrent_tests, schedule, line_num, scbuf);
  			exit(2);
  		}
  		else if (max_connections > 0 && max_connections < num_tests)
  		{
  			int			oldest = 0;
  
  			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)
  				{
  					wait_for_tests(pids + oldest, statuses + oldest,
  								   tests + oldest, i - oldest);
  					oldest = i;
  				}
  				pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
  			}
  			wait_for_tests(pids + oldest, statuses + oldest,
  						   tests + oldest, i - oldest);
  			status_end();
  		}
  		else
  		{
  			status(_("parallel group (%d tests): "), num_tests);
  			for (i = 0; i < num_tests; i++)
  			{
  				pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
  			}
  			wait_for_tests(pids, statuses, tests, num_tests);
  			status_end();
  		}
  
  		/* Check results for all tests */
  		for (i = 0; i < num_tests; i++)
  		{
  			_stringlist *rl,
  					   *el,
  					   *tl;
  			bool		differ = false;
  			/* POLAR */
  			bool polar_ignore = false;
  			/* POLAR end */
  
  			if (num_tests > 1)
  				status(_("     %-28s ... "), tests[i]);
  
  			/*
  			 * Advance over all three lists simultaneously.
  			 *
  			 * Compare resultfiles[j] with expectfiles[j] always. Tags are
  			 * optional but if there are tags, the tag list has the same
  			 * length as the other two lists.
  			 */
  			for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
  				 rl != NULL;	/* rl and el have the same length */
  				 rl = rl->next, el = el->next,
  				 tl = tl ? tl->next : NULL)
  			{
  				bool		newdiff;
  				/* POLAR */
  				_stringlist *sl;
  
  				if (polar_is_enable)
  				{
  					for (sl = polar_ignorelist; sl != NULL; sl = sl->next)
  					{
  						if (strcmp(tests[i], sl->str) == 0)
  						{
  							polar_ignore = true;
  							break;
  						}
  					}
  				}
  				if (polar_ignore)
  					continue;
  				/* POLAR end */
  
  				newdiff = results_differ(tests[i], rl->str, el->str);
  				if (newdiff && tl)
  				{
  					printf("%s ", tl->str);
  				}
  				differ |= newdiff;
  			}
  
  			if (differ)
  			{
  				bool		ignore = false;
  				_stringlist *sl;
  
  				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
  			{
  				/* POLAR */
  				if (polar_ignore)
  					status(_("ok (polar ignored)"));
  				else
  					status(_("ok"));
  				/* POLAR end */
  				success_count++;
  			}
  
  			if (statuses[i] != 0)
  				log_child_failure(statuses[i]);
  
  			status_end();
  		}
  
  		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]);
  		}
  	}
  
  	free_stringlist(&ignorelist);
  	free_stringlist(&polar_ignorelist);
  
  	fclose(scf);
  }
  
  /*
   * Run a single test
   */
  static void
  run_single_test(const char *test, test_function tfunc)
  {
  	PID_TYPE	pid;
  	int			exit_status;
  	_stringlist *resultfiles = NULL;
  	_stringlist *expectfiles = NULL;
  	_stringlist *tags = NULL;
  	_stringlist *rl,
  			   *el,
  			   *tl;
  	bool		differ = false;
  
  	status(_("test %-28s ... "), test);
  	pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
  	wait_for_tests(&pid, &exit_status, NULL, 1);
  
  	/*
  	 * Advance over all three lists simultaneously.
  	 *
  	 * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
  	 * but if there are tags, the tag list has the same length as the other
  	 * two lists.
  	 */
  	for (rl = resultfiles, el = expectfiles, tl = tags;
  		 rl != NULL;			/* rl and el have the same length */
  		 rl = rl->next, el = el->next,
  		 tl = tl ? tl->next : NULL)
  	{
  		bool		newdiff;
  
  		newdiff = results_differ(test, rl->str, el->str);
  		if (newdiff && tl)
  		{
  			printf("%s ", tl->str);
  		}
  		differ |= newdiff;
  	}
  
  	if (differ)
  	{
  		status(_("FAILED"));
  		fail_count++;
  	}
  	else
  	{
  		status(_("ok"));
  		success_count++;
  	}
  
  	if (exit_status != 0)
  		log_child_failure(exit_status);
  
  	status_end();
  }
  
  /*
   * Create the summary-output files (making them empty if already existing)
   */
  static void
  open_result_files(void)
  {
  	char		file[MAXPGPATH];
  	FILE	   *difffile;
  
  	/* create outputdir directory if not present */
  	if (!directory_exists(outputdir))
  		make_directory(outputdir);
  
  	/* create the log file (copy of running status output) */
  	snprintf(file, sizeof(file), "%s/regression.out", outputdir);
  	logfilename = pg_strdup(file);
  	logfile = fopen(logfilename, "w");
  	if (!logfile)
  	{
  		fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  				progname, logfilename, strerror(errno));
  		exit(2);
  	}
  
  	/* create the diffs file as empty */
  	snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
  	difffilename = pg_strdup(file);
  	difffile = fopen(difffilename, "w");
  	if (!difffile)
  	{
  		fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  				progname, difffilename, strerror(errno));
  		exit(2);
  	}
  	/* we don't keep the diffs file open continuously */
  	fclose(difffile);
  
  	/* also create the results directory if not present */
  	snprintf(file, sizeof(file), "%s/results", outputdir);
  	if (!directory_exists(file))
  		make_directory(file);
  }
  
  static void
  drop_database_if_exists(const char *dbname)
  {
  	header(_("dropping database \"%s\""), dbname);
  	psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
  }
  
  static void
  create_database(const char *dbname)
  {
  	_stringlist *sl;
  
  	/*
  	 * We use template0 so that any installation-local cruft in template1 will
  	 * not mess up the tests.
  	 */
  	header(_("creating database \"%s\""), dbname);
  	if (encoding)
  		psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
  					 (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
  	else
  		psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
  					 (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
  	psql_command(dbname,
  				 "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
  				 "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
  				 "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
  				 "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
  				 "ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
  				 "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
  				 dbname, dbname, dbname, dbname, dbname, dbname);
  
  	/*
  	 * Install any requested procedural languages.  We use CREATE OR REPLACE
  	 * so that this will work whether or not the language is preinstalled.
  	 */
  	for (sl = loadlanguage; sl != NULL; sl = sl->next)
  	{
  		header(_("installing %s"), sl->str);
  		psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
  	}
  
  	/*
  	 * Install any requested extensions.  We use CREATE IF NOT EXISTS so that
  	 * this will work whether or not the extension is preinstalled.
  	 */
  	for (sl = loadextension; sl != NULL; sl = sl->next)
  	{
  		header(_("installing %s"), sl->str);
  		psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
  	}
  }
  
  static void
  drop_role_if_exists(const char *rolename)
  {
  	header(_("dropping role \"%s\""), rolename);
  	psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
  }
  
  static void
  create_role(const char *rolename, const _stringlist *granted_dbs)
  {
  	header(_("creating role \"%s\""), rolename);
  	psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
  	for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
  	{
  		psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
  					 granted_dbs->str, rolename);
  	}
  }
  
  static void
  help(void)
  {
  	printf(_("PostgreSQL regression test driver\n"));
  	printf(_("\n"));
  	printf(_("Usage:\n  %s [OPTION]... [EXTRA-TEST]...\n"), progname);
  	printf(_("\n"));
  	printf(_("Options:\n"));
  	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"));
  	printf(_("\n"));
  	printf(_("Options for \"temp-instance\" mode:\n"));
  	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"));
  	printf(_("\n"));
  	printf(_("Options for using an existing installation:\n"));
  	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"));
  	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"));
  	printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
  }
  
  int
  regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
  {
  	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},
  		{"encoding", required_argument, NULL, 6},
  		{"outputdir", required_argument, NULL, 7},
  		{"schedule", required_argument, NULL, 8},
  		{"temp-instance", required_argument, NULL, 9},
  		{"no-locale", no_argument, NULL, 10},
  		{"host", required_argument, NULL, 13},
  		{"port", required_argument, NULL, 14},
  		{"user", required_argument, NULL, 15},
  		{"bindir", required_argument, NULL, 16},
  		{"dlpath", required_argument, NULL, 17},
  		{"create-role", required_argument, NULL, 18},
  		{"temp-config", required_argument, NULL, 19},
  		{"use-existing", no_argument, NULL, 20},
  		{"launcher", required_argument, NULL, 21},
  		{"load-extension", required_argument, NULL, 22},
  		{"config-auth", required_argument, NULL, 24},
  		{"max-concurrent-tests", required_argument, NULL, 25},
  		{NULL, 0, NULL, 0}
  	};
  
  	_stringlist *sl;
  	int			c;
  	int			i;
  	int			option_index;
  	char		buf[MAXPGPATH * 4];
  	char		buf2[MAXPGPATH * 4];
  	/* POLAR */
  	char    polar_buf[MAXPGPATH * 4];
  
  	progname = get_progname(argv[0]);
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
  
  	atexit(stop_postmaster);
  
  #ifndef HAVE_UNIX_SOCKETS
  	/* no unix domain sockets available, so change default */
  	hostname = "localhost";
  #endif
  
  	/*
  	 * We call the initialization function here because that way we can set
  	 * default parameters and let them be overwritten by the commandline.
  	 */
  	ifunc(argc, argv);
  
  	if (getenv("PG_REGRESS_DIFF_OPTS"))
  		pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
  
  	while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
  	{
  		switch (c)
  		{
  			case 'h':
  				help();
  				exit(0);
  			case 'V':
  				puts("pg_regress (PostgreSQL) " PG_VERSION);
  				exit(0);
  			case 1:
  
  				/*
  				 * If a default database was specified, we need to remove it
  				 * before we add the specified one.
  				 */
  				free_stringlist(&dblist);
  				split_to_stringlist(optarg, ",", &dblist);
  				break;
  			case 2:
  				debug = true;
  				break;
  			case 3:
  				inputdir = pg_strdup(optarg);
  				break;
  			case 4:
  				add_stringlist_item(&loadlanguage, optarg);
  				break;
  			case 5:
  				max_connections = atoi(optarg);
  				break;
  			case 6:
  				encoding = pg_strdup(optarg);
  				break;
  			case 7:
  				outputdir = pg_strdup(optarg);
  				break;
  			case 8:
  				add_stringlist_item(&schedulelist, optarg);
  				break;
  			case 9:
  				temp_instance = make_absolute_path(optarg);
  				break;
  			case 10:
  				nolocale = true;
  				break;
  			case 13:
  				hostname = pg_strdup(optarg);
  				break;
  			case 14:
  				port = atoi(optarg);
  				port_specified_by_user = true;
  				break;
  			case 15:
  				user = pg_strdup(optarg);
  				break;
  			case 16:
  				/* "--bindir=" means to use PATH */
  				if (strlen(optarg))
  					bindir = pg_strdup(optarg);
  				else
  					bindir = NULL;
  				break;
  			case 17:
  				dlpath = pg_strdup(optarg);
  				break;
  			case 18:
  				split_to_stringlist(optarg, ",", &extraroles);
  				break;
  			case 19:
  				add_stringlist_item(&temp_configs, optarg);
  				break;
  			case 20:
  				use_existing = true;
  				break;
  			case 21:
  				launcher = pg_strdup(optarg);
  				break;
  			case 22:
  				add_stringlist_item(&loadextension, optarg);
  				break;
  			case 24:
  				config_auth_datadir = pg_strdup(optarg);
  				break;
  			case 25:
  				max_concurrent_tests = atoi(optarg);
  				break;
  			default:
  				/* getopt_long already emitted a complaint */
  				fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
  						progname);
  				exit(2);
  		}
  	}
  
  	/*
  	 * if we still have arguments, they are extra tests to run
  	 */
  	while (argc - optind >= 1)
  	{
  		add_stringlist_item(&extra_tests, argv[optind]);
  		optind++;
  	}
  
  	if (config_auth_datadir)
  	{
  #ifdef ENABLE_SSPI
  		config_sspi_auth(config_auth_datadir);
  #endif
  		exit(0);
  	}
  
  	if (temp_instance && !port_specified_by_user)
  
  		/*
  		 * To reduce chances of interference with parallel installations, use
  		 * a port number starting in the private range (49152-65535)
  		 * calculated from the version number.  This aids !HAVE_UNIX_SOCKETS
  		 * systems; elsewhere, the use of a private socket directory already
  		 * prevents interference.
  		 */
  		port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
  
  	inputdir = make_absolute_path(inputdir);
  	outputdir = make_absolute_path(outputdir);
  	dlpath = make_absolute_path(dlpath);
  
  	/*
  	 * Initialization
  	 */
  	open_result_files();
  
  	initialize_environment();
  
  #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
  	unlimit_core_size();
  #endif
  
  	if (temp_instance)
  	{
  		FILE	   *pg_conf;
  		const char *env_wait;
  		int			wait_seconds;
  
  		/*
  		 * Prepare the temp instance
  		 */
  
  		if (directory_exists(temp_instance))
  		{
  			header(_("removing existing temp instance"));
  			if (!rmtree(temp_instance, true))
  			{
  				fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
  						progname, temp_instance);
  				exit(2);
  			}
  		}
  
  		header(_("creating temporary instance"));
  
  		/* make the temp instance top directory */
  		make_directory(temp_instance);
  
  		/* and a directory for log files */
  		snprintf(buf, sizeof(buf), "%s/log", outputdir);
  		if (!directory_exists(buf))
  			make_directory(buf);
  
  		/* initdb */
  		header(_("initializing database system"));
  		snprintf(buf, sizeof(buf),
  				 "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1",
  				 bindir ? bindir : "",
  				 bindir ? "/" : "",
  				 temp_instance,
  				 debug ? " --debug" : "",
  				 nolocale ? " --no-locale" : "",
  				 outputdir);
  		if (system(buf))
  		{
  			fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
  			exit(2);
  		}
  
  		/*
  		 * 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.)
  		 */
  		snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
  		pg_conf = fopen(buf, "a");
  		if (pg_conf == NULL)
  		{
  			fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
  			exit(2);
  		}
  		fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
  		fputs("log_autovacuum_min_duration = 0\n", pg_conf);
  		fputs("log_checkpoints = on\n", pg_conf);
  		fputs("log_line_prefix = '%m [%p] %q%a '\n", pg_conf);
  		fputs("log_lock_waits = on\n", pg_conf);
  		fputs("log_temp_files = 128kB\n", pg_conf);
  		fputs("max_prepared_transactions = 2\n", pg_conf);
  
  		for (sl = temp_configs; sl != NULL; sl = sl->next)
  		{
  			char	   *temp_config = sl->str;
  			FILE	   *extra_conf;
  			char		line_buf[1024];
  
  			extra_conf = fopen(temp_config, "r");
  			if (extra_conf == NULL)
  			{
  				fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno));
  				exit(2);
  			}
  			while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
  				fputs(line_buf, pg_conf);
  			fclose(extra_conf);
  		}
  
  		fclose(pg_conf);
  
  #ifdef ENABLE_SSPI
  
  		/*
  		 * Since we successfully used the same buffer for the much-longer
  		 * "initdb" command, this can't truncate.
  		 */
  		snprintf(buf, sizeof(buf), "%s/data", temp_instance);
  		config_sspi_auth(buf);
  #elif !defined(HAVE_UNIX_SOCKETS)
  #error Platform has no means to secure the test installation.
  #endif
  
  		/*
  		 * Check if there is a postmaster running already.
  		 */
  		snprintf(buf2, sizeof(buf2),
  				 "\"%s%spsql\" -X postgres <%s 2>%s",
  				 bindir ? bindir : "",
  				 bindir ? "/" : "",
  				 DEVNULL, DEVNULL);
  
  		for (i = 0; i < 16; i++)
  		{
  			if (system(buf2) == 0)
  			{
  				char		s[16];
  
  				if (port_specified_by_user || i == 15)
  				{
  					fprintf(stderr, _("port %d apparently in use\n"), port);
  					if (!port_specified_by_user)
  						fprintf(stderr, _("%s: could not determine an available port\n"), progname);
  					fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n"));
  					exit(2);
  				}
  
  				fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1);
  				port++;
  				sprintf(s, "%d", port);
  				doputenv("PGPORT", s);
  			}
  			else
  				break;
  		}
  
  		/*
  		 * Start the temp postmaster
  		 */
  		header(_("starting postmaster"));
  		snprintf(buf, sizeof(buf),
  				 "\"%s%spostgres\" -D \"%s/data\" -F%s "
  				 "-c \"listen_addresses=%s\" -k \"%s\" "
  				 "> \"%s/log/postmaster.log\" 2>&1",
  				 bindir ? bindir : "",
  				 bindir ? "/" : "",
  				 temp_instance, debug ? " -d 5" : "",
  				 hostname ? hostname : "", sockdir ? sockdir : "",
  				 outputdir);
  		postmaster_pid = spawn_process(buf);
  		if (postmaster_pid == INVALID_PID)
  		{
  			fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"),
  					progname, strerror(errno));
  			exit(2);
  		}
  
  		/*
  		 * 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.
  		 */
  		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++)
  		{
  			/* Done if psql succeeds */
  			if (system(buf2) == 0)
  				break;
  
  			/*
  			 * Fail immediately if postmaster has exited
  			 */
  #ifndef WIN32
  			if (kill(postmaster_pid, 0) != 0)
  #else
  			if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
  #endif
  			{
  				fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
  				exit(2);
  			}
  
  			pg_usleep(1000000L);
  		}
  		if (i >= wait_seconds)
  		{
  			fprintf(stderr, _("\n%s: postmaster did not respond within %d seconds\nExamine %s/log/postmaster.log for the reason\n"),
  					progname, wait_seconds, outputdir);
  
  			/*
  			 * 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
  			 * 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));
  #else
  			if (TerminateProcess(postmaster_pid, 255) == 0)
  				fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n"),
  						progname, GetLastError());
  #endif
  
  			exit(2);
  		}
  
  		postmaster_running = true;
  
  #ifdef _WIN64
  /* need a series of two casts to convert HANDLE without compiler warning */
  #define ULONGPID(x) (unsigned long) (unsigned long long) (x)
  #else
  #define ULONGPID(x) (unsigned long) (x)
  #endif
  		printf(_("running on port %d with PID %lu\n"),
  			   port, ULONGPID(postmaster_pid));
  	}
  	else
  	{
  		/*
  		 * Using an existing installation, so may need to get rid of
  		 * pre-existing database(s) and role(s)
  		 */
  		if (!use_existing)
  		{
  			for (sl = dblist; sl; sl = sl->next)
  				drop_database_if_exists(sl->str);
  			for (sl = extraroles; sl; sl = sl->next)
  				drop_role_if_exists(sl->str);
  		}
  	}
  
  	/*
  	 * Create the test database(s) and role(s)
  	 */
  	if (!use_existing)
  	{
  		for (sl = dblist; sl; sl = sl->next)
  			create_database(sl->str);
  		for (sl = extraroles; sl; sl = sl->next)
  			create_role(sl->str, dblist);
  	}
  
  	/*
  	 * Ready to run the tests
  	 */
  	header(_("running regression test queries"));
  
  	for (sl = schedulelist; sl != NULL; sl = sl->next)
  	{
  		run_schedule(sl->str, tfunc);
  	}
  
  	for (sl = extra_tests; sl != NULL; sl = sl->next)
  	{
  		run_single_test(sl->str, tfunc);
  	}
  
  	/*
  	 * Shut down temp installation's postmaster
  	 */
  	if (temp_instance)
  	{
  		header(_("shutting down postmaster"));
  		stop_postmaster();
  	}
  
  	/*
  	 * If there were no errors, remove the temp instance immediately to
  	 * conserve disk space.  (If there were errors, we leave the instance in
  	 * place for possible manual investigation.)
  	 */
  	if (temp_instance && fail_count == 0 && fail_ignore_count == 0)
  	{
  		header(_("removing temporary instance"));
  		if (!rmtree(temp_instance, true))
  			fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
  					progname, temp_instance);
  	}
  
  	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);
  	else if (fail_count == 0)	/* fail_count=0, fail_ignore_count>0 */
  		snprintf(buf, sizeof(buf),
  				 _(" %d of %d tests passed, %d 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 */
  		snprintf(buf, sizeof(buf),
  				 _(" %d of %d tests failed. "),
  				 fail_count,
  				 success_count + fail_count);
  	else
  		/* fail_count>0 && fail_ignore_count>0 */
  		snprintf(buf, sizeof(buf),
  				 _(" %d of %d tests failed, %d of these failures ignored. "),
  				 fail_count + fail_ignore_count,
  				 success_count + fail_count + fail_ignore_count,
  				 fail_ignore_count);
  
  	putchar('\n');
  
  	/* POLAR */
  	snprintf(polar_buf, sizeof(polar_buf),
  			_(" All %d tests, %d tests in ignore, %d tests in polar ignore. "),
  			success_count + polar_ignore_count + ignore_count,
  			ignore_count,
  			polar_ignore_count);
  
  	putchar('\n');
  
  	/* POLAR use strlen(polar_buf) */
  	for (i = strlen(polar_buf); i > 0; i--)
  		putchar('=');
  	printf("\n%s\n", buf);
  	/* POLAR */
  	printf("\n POLARDB:\n%s\n", polar_buf);
  	/* POLAR end*/
  	for (i = strlen(polar_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"),
  			   difffilename, logfilename);
  	}
  	else
  	{
  		unlink(difffilename);
  		unlink(logfilename);
  	}
  
  	if (fail_count != 0)
  		exit(1);
  
  	return 0;
  }
  