| 1 | /*------------------------------------------------------------------------- | 
| 2 |  * | 
| 3 |  * pg_regress --- regression test driver | 
| 4 |  * | 
| 5 |  * This is a C implementation of the previous shell script for running | 
| 6 |  * the regression tests, and should be mostly compatible with it. | 
| 7 |  * Initial author of C translation: Magnus Hagander | 
| 8 |  * | 
| 9 |  * This code is released under the terms of the PostgreSQL License. | 
| 10 |  * | 
| 11 |  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group | 
| 12 |  * Portions Copyright (c) 1994, Regents of the University of California | 
| 13 |  * | 
| 14 |  * src/test/regress/pg_regress.c | 
| 15 |  * | 
| 16 |  *------------------------------------------------------------------------- | 
| 17 |  */ | 
| 18 |  | 
| 19 | #include "postgres_fe.h" | 
| 20 |  | 
| 21 | #include <ctype.h> | 
| 22 | #include <sys/stat.h> | 
| 23 | #include <sys/wait.h> | 
| 24 | #include <signal.h> | 
| 25 | #include <unistd.h> | 
| 26 |  | 
| 27 | #ifdef HAVE_SYS_RESOURCE_H | 
| 28 | #include <sys/time.h> | 
| 29 | #include <sys/resource.h> | 
| 30 | #endif | 
| 31 |  | 
| 32 | #include "pg_regress.h" | 
| 33 |  | 
| 34 | #include "common/logging.h" | 
| 35 | #include "common/restricted_token.h" | 
| 36 | #include "common/username.h" | 
| 37 | #include "getopt_long.h" | 
| 38 | #include "libpq/pqcomm.h"		/* needed for UNIXSOCK_PATH() */ | 
| 39 | #include "pg_config_paths.h" | 
| 40 | #include "portability/instr_time.h" | 
| 41 |  | 
| 42 | /* for resultmap we need a list of pairs of strings */ | 
| 43 | typedef struct _resultmap | 
| 44 | { | 
| 45 | 	char	   *test; | 
| 46 | 	char	   *type; | 
| 47 | 	char	   *resultfile; | 
| 48 | 	struct _resultmap *next; | 
| 49 | } _resultmap; | 
| 50 |  | 
| 51 | /* | 
| 52 |  * Values obtained from Makefile. | 
| 53 |  */ | 
| 54 | char	   *host_platform = HOST_TUPLE; | 
| 55 |  | 
| 56 | #ifndef WIN32					/* not used in WIN32 case */ | 
| 57 | static char *shellprog = SHELLPROG; | 
| 58 | #endif | 
| 59 |  | 
| 60 | /* | 
| 61 |  * On Windows we use -w in diff switches to avoid problems with inconsistent | 
| 62 |  * newline representation.  The actual result files will generally have | 
| 63 |  * Windows-style newlines, but the comparison files might or might not. | 
| 64 |  */ | 
| 65 | #ifndef WIN32 | 
| 66 | const char *basic_diff_opts = "" ; | 
| 67 | const char *pretty_diff_opts = "-U3" ; | 
| 68 | #else | 
| 69 | const char *basic_diff_opts = "-w" ; | 
| 70 | const char *pretty_diff_opts = "-w -U3" ; | 
| 71 | #endif | 
| 72 |  | 
| 73 | /* options settable from command line */ | 
| 74 | _stringlist *dblist = NULL; | 
| 75 | bool		debug = false; | 
| 76 | char	   *inputdir = "." ; | 
| 77 | char	   *outputdir = "." ; | 
| 78 | char	   *bindir = PGBINDIR; | 
| 79 | char	   *launcher = NULL; | 
| 80 | static _stringlist *loadlanguage = NULL; | 
| 81 | static _stringlist *loadextension = NULL; | 
| 82 | static int	max_connections = 0; | 
| 83 | static int	max_concurrent_tests = 0; | 
| 84 | static char *encoding = NULL; | 
| 85 | static _stringlist *schedulelist = NULL; | 
| 86 | static _stringlist * = NULL; | 
| 87 | static char *temp_instance = NULL; | 
| 88 | static _stringlist *temp_configs = NULL; | 
| 89 | static bool nolocale = false; | 
| 90 | static bool use_existing = false; | 
| 91 | static char *hostname = NULL; | 
| 92 | static int	port = -1; | 
| 93 | static bool port_specified_by_user = false; | 
| 94 | static char *dlpath = PKGLIBDIR; | 
| 95 | static char *user = NULL; | 
| 96 | static _stringlist * = NULL; | 
| 97 | static char *config_auth_datadir = NULL; | 
| 98 |  | 
| 99 | /* internal variables */ | 
| 100 | static const char *progname; | 
| 101 | static char *logfilename; | 
| 102 | static FILE *logfile; | 
| 103 | static char *difffilename; | 
| 104 | static const char *sockdir; | 
| 105 | #ifdef HAVE_UNIX_SOCKETS | 
| 106 | static const char *temp_sockdir; | 
| 107 | static char sockself[MAXPGPATH]; | 
| 108 | static char socklock[MAXPGPATH]; | 
| 109 | #endif | 
| 110 |  | 
| 111 | static _resultmap *resultmap = NULL; | 
| 112 |  | 
| 113 | static PID_TYPE postmaster_pid = INVALID_PID; | 
| 114 | static bool postmaster_running = false; | 
| 115 |  | 
| 116 | static int	success_count = 0; | 
| 117 | static int	fail_count = 0; | 
| 118 | static int	fail_ignore_count = 0; | 
| 119 |  | 
| 120 | static bool directory_exists(const char *dir); | 
| 121 | static void make_directory(const char *dir); | 
| 122 |  | 
| 123 | static void header(const char *fmt,...) pg_attribute_printf(1, 2); | 
| 124 | static void status(const char *fmt,...) pg_attribute_printf(1, 2); | 
| 125 | static void psql_command(const char *database, const char *query,...) pg_attribute_printf(2, 3); | 
| 126 |  | 
| 127 | /* | 
| 128 |  * allow core files if possible. | 
| 129 |  */ | 
| 130 | #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) | 
| 131 | static void | 
| 132 | unlimit_core_size(void) | 
| 133 | { | 
| 134 | 	struct rlimit lim; | 
| 135 |  | 
| 136 | 	getrlimit(RLIMIT_CORE, &lim); | 
| 137 | 	if (lim.rlim_max == 0) | 
| 138 | 	{ | 
| 139 | 		fprintf(stderr, | 
| 140 | 				_("%s: could not set core size: disallowed by hard limit\n" ), | 
| 141 | 				progname); | 
| 142 | 		return; | 
| 143 | 	} | 
| 144 | 	else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max) | 
| 145 | 	{ | 
| 146 | 		lim.rlim_cur = lim.rlim_max; | 
| 147 | 		setrlimit(RLIMIT_CORE, &lim); | 
| 148 | 	} | 
| 149 | } | 
| 150 | #endif | 
| 151 |  | 
| 152 |  | 
| 153 | /* | 
| 154 |  * Add an item at the end of a stringlist. | 
| 155 |  */ | 
| 156 | void | 
| 157 | add_stringlist_item(_stringlist **listhead, const char *str) | 
| 158 | { | 
| 159 | 	_stringlist *newentry = pg_malloc(sizeof(_stringlist)); | 
| 160 | 	_stringlist *oldentry; | 
| 161 |  | 
| 162 | 	newentry->str = pg_strdup(str); | 
| 163 | 	newentry->next = NULL; | 
| 164 | 	if (*listhead == NULL) | 
| 165 | 		*listhead = newentry; | 
| 166 | 	else | 
| 167 | 	{ | 
| 168 | 		for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next) | 
| 169 | 			 /* skip */ ; | 
| 170 | 		oldentry->next = newentry; | 
| 171 | 	} | 
| 172 | } | 
| 173 |  | 
| 174 | /* | 
| 175 |  * Free a stringlist. | 
| 176 |  */ | 
| 177 | static void | 
| 178 | free_stringlist(_stringlist **listhead) | 
| 179 | { | 
| 180 | 	if (listhead == NULL || *listhead == NULL) | 
| 181 | 		return; | 
| 182 | 	if ((*listhead)->next != NULL) | 
| 183 | 		free_stringlist(&((*listhead)->next)); | 
| 184 | 	free((*listhead)->str); | 
| 185 | 	free(*listhead); | 
| 186 | 	*listhead = NULL; | 
| 187 | } | 
| 188 |  | 
| 189 | /* | 
| 190 |  * Split a delimited string into a stringlist | 
| 191 |  */ | 
| 192 | static void | 
| 193 | split_to_stringlist(const char *s, const char *delim, _stringlist **listhead) | 
| 194 | { | 
| 195 | 	char	   *sc = pg_strdup(s); | 
| 196 | 	char	   *token = strtok(sc, delim); | 
| 197 |  | 
| 198 | 	while (token) | 
| 199 | 	{ | 
| 200 | 		add_stringlist_item(listhead, token); | 
| 201 | 		token = strtok(NULL, delim); | 
| 202 | 	} | 
| 203 | 	free(sc); | 
| 204 | } | 
| 205 |  | 
| 206 | /* | 
| 207 |  * Print a progress banner on stdout. | 
| 208 |  */ | 
| 209 | static void | 
| 210 | (const char *fmt,...) | 
| 211 | { | 
| 212 | 	char		tmp[64]; | 
| 213 | 	va_list		ap; | 
| 214 |  | 
| 215 | 	va_start(ap, fmt); | 
| 216 | 	vsnprintf(tmp, sizeof(tmp), fmt, ap); | 
| 217 | 	va_end(ap); | 
| 218 |  | 
| 219 | 	fprintf(stdout, "============== %-38s ==============\n" , tmp); | 
| 220 | 	fflush(stdout); | 
| 221 | } | 
| 222 |  | 
| 223 | /* | 
| 224 |  * Print "doing something ..." --- supplied text should not end with newline | 
| 225 |  */ | 
| 226 | static void | 
| 227 | status(const char *fmt,...) | 
| 228 | { | 
| 229 | 	va_list		ap; | 
| 230 |  | 
| 231 | 	va_start(ap, fmt); | 
| 232 | 	vfprintf(stdout, fmt, ap); | 
| 233 | 	fflush(stdout); | 
| 234 | 	va_end(ap); | 
| 235 |  | 
| 236 | 	if (logfile) | 
| 237 | 	{ | 
| 238 | 		va_start(ap, fmt); | 
| 239 | 		vfprintf(logfile, fmt, ap); | 
| 240 | 		va_end(ap); | 
| 241 | 	} | 
| 242 | } | 
| 243 |  | 
| 244 | /* | 
| 245 |  * Done "doing something ..." | 
| 246 |  */ | 
| 247 | static void | 
| 248 | status_end(void) | 
| 249 | { | 
| 250 | 	fprintf(stdout, "\n" ); | 
| 251 | 	fflush(stdout); | 
| 252 | 	if (logfile) | 
| 253 | 		fprintf(logfile, "\n" ); | 
| 254 | } | 
| 255 |  | 
| 256 | /* | 
| 257 |  * shut down temp postmaster | 
| 258 |  */ | 
| 259 | static void | 
| 260 | stop_postmaster(void) | 
| 261 | { | 
| 262 | 	if (postmaster_running) | 
| 263 | 	{ | 
| 264 | 		/* We use pg_ctl to issue the kill and wait for stop */ | 
| 265 | 		char		buf[MAXPGPATH * 2]; | 
| 266 | 		int			r; | 
| 267 |  | 
| 268 | 		/* On Windows, system() seems not to force fflush, so... */ | 
| 269 | 		fflush(stdout); | 
| 270 | 		fflush(stderr); | 
| 271 |  | 
| 272 | 		snprintf(buf, sizeof(buf), | 
| 273 | 				 "\"%s%spg_ctl\" stop -D \"%s/data\" -s" , | 
| 274 | 				 bindir ? bindir : "" , | 
| 275 | 				 bindir ? "/"  : "" , | 
| 276 | 				 temp_instance); | 
| 277 | 		r = system(buf); | 
| 278 | 		if (r != 0) | 
| 279 | 		{ | 
| 280 | 			fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n" ), | 
| 281 | 					progname, r); | 
| 282 | 			_exit(2);			/* not exit(), that could be recursive */ | 
| 283 | 		} | 
| 284 |  | 
| 285 | 		postmaster_running = false; | 
| 286 | 	} | 
| 287 | } | 
| 288 |  | 
| 289 | #ifdef HAVE_UNIX_SOCKETS | 
| 290 | /* | 
| 291 |  * Remove the socket temporary directory.  pg_regress never waits for a | 
| 292 |  * postmaster exit, so it is indeterminate whether the postmaster has yet to | 
| 293 |  * unlink the socket and lock file.  Unlink them here so we can proceed to | 
| 294 |  * remove the directory.  Ignore errors; leaking a temporary directory is | 
| 295 |  * unimportant.  This can run from a signal handler.  The code is not | 
| 296 |  * acceptable in a Windows signal handler (see initdb.c:trapsig()), but | 
| 297 |  * Windows is not a HAVE_UNIX_SOCKETS platform. | 
| 298 |  */ | 
| 299 | static void | 
| 300 | remove_temp(void) | 
| 301 | { | 
| 302 | 	Assert(temp_sockdir); | 
| 303 | 	unlink(sockself); | 
| 304 | 	unlink(socklock); | 
| 305 | 	rmdir(temp_sockdir); | 
| 306 | } | 
| 307 |  | 
| 308 | /* | 
| 309 |  * Signal handler that calls remove_temp() and reraises the signal. | 
| 310 |  */ | 
| 311 | static void | 
| 312 | signal_remove_temp(int signum) | 
| 313 | { | 
| 314 | 	remove_temp(); | 
| 315 |  | 
| 316 | 	pqsignal(signum, SIG_DFL); | 
| 317 | 	raise(signum); | 
| 318 | } | 
| 319 |  | 
| 320 | /* | 
| 321 |  * Create a temporary directory suitable for the server's Unix-domain socket. | 
| 322 |  * The directory will have mode 0700 or stricter, so no other OS user can open | 
| 323 |  * our socket to exploit our use of trust authentication.  Most systems | 
| 324 |  * constrain the length of socket paths well below _POSIX_PATH_MAX, so we | 
| 325 |  * place the directory under /tmp rather than relative to the possibly-deep | 
| 326 |  * current working directory. | 
| 327 |  * | 
| 328 |  * Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits | 
| 329 |  * testing to work in builds that relocate it to a directory not writable to | 
| 330 |  * the build/test user. | 
| 331 |  */ | 
| 332 | static const char * | 
| 333 | make_temp_sockdir(void) | 
| 334 | { | 
| 335 | 	char	   *template = pg_strdup("/tmp/pg_regress-XXXXXX" ); | 
| 336 |  | 
| 337 | 	temp_sockdir = mkdtemp(template); | 
| 338 | 	if (temp_sockdir == NULL) | 
| 339 | 	{ | 
| 340 | 		fprintf(stderr, _("%s: could not create directory \"%s\": %s\n" ), | 
| 341 | 				progname, template, strerror(errno)); | 
| 342 | 		exit(2); | 
| 343 | 	} | 
| 344 |  | 
| 345 | 	/* Stage file names for remove_temp().  Unsafe in a signal handler. */ | 
| 346 | 	UNIXSOCK_PATH(sockself, port, temp_sockdir); | 
| 347 | 	snprintf(socklock, sizeof(socklock), "%s.lock" , sockself); | 
| 348 |  | 
| 349 | 	/* Remove the directory during clean exit. */ | 
| 350 | 	atexit(remove_temp); | 
| 351 |  | 
| 352 | 	/* | 
| 353 | 	 * Remove the directory before dying to the usual signals.  Omit SIGQUIT, | 
| 354 | 	 * preserving it as a quick, untidy exit. | 
| 355 | 	 */ | 
| 356 | 	pqsignal(SIGHUP, signal_remove_temp); | 
| 357 | 	pqsignal(SIGINT, signal_remove_temp); | 
| 358 | 	pqsignal(SIGPIPE, signal_remove_temp); | 
| 359 | 	pqsignal(SIGTERM, signal_remove_temp); | 
| 360 |  | 
| 361 | 	return temp_sockdir; | 
| 362 | } | 
| 363 | #endif							/* HAVE_UNIX_SOCKETS */ | 
| 364 |  | 
| 365 | /* | 
| 366 |  * Check whether string matches pattern | 
| 367 |  * | 
| 368 |  * In the original shell script, this function was implemented using expr(1), | 
| 369 |  * which provides basic regular expressions restricted to match starting at | 
| 370 |  * the string start (in conventional regex terms, there's an implicit "^" | 
| 371 |  * at the start of the pattern --- but no implicit "$" at the end). | 
| 372 |  * | 
| 373 |  * For now, we only support "." and ".*" as non-literal metacharacters, | 
| 374 |  * because that's all that anyone has found use for in resultmap.  This | 
| 375 |  * code could be extended if more functionality is needed. | 
| 376 |  */ | 
| 377 | static bool | 
| 378 | string_matches_pattern(const char *str, const char *pattern) | 
| 379 | { | 
| 380 | 	while (*str && *pattern) | 
| 381 | 	{ | 
| 382 | 		if (*pattern == '.' && pattern[1] == '*') | 
| 383 | 		{ | 
| 384 | 			pattern += 2; | 
| 385 | 			/* Trailing .* matches everything. */ | 
| 386 | 			if (*pattern == '\0') | 
| 387 | 				return true; | 
| 388 |  | 
| 389 | 			/* | 
| 390 | 			 * Otherwise, scan for a text position at which we can match the | 
| 391 | 			 * rest of the pattern. | 
| 392 | 			 */ | 
| 393 | 			while (*str) | 
| 394 | 			{ | 
| 395 | 				/* | 
| 396 | 				 * Optimization to prevent most recursion: don't recurse | 
| 397 | 				 * unless first pattern char might match this text char. | 
| 398 | 				 */ | 
| 399 | 				if (*str == *pattern || *pattern == '.') | 
| 400 | 				{ | 
| 401 | 					if (string_matches_pattern(str, pattern)) | 
| 402 | 						return true; | 
| 403 | 				} | 
| 404 |  | 
| 405 | 				str++; | 
| 406 | 			} | 
| 407 |  | 
| 408 | 			/* | 
| 409 | 			 * End of text with no match. | 
| 410 | 			 */ | 
| 411 | 			return false; | 
| 412 | 		} | 
| 413 | 		else if (*pattern != '.' && *str != *pattern) | 
| 414 | 		{ | 
| 415 | 			/* | 
| 416 | 			 * Not the single-character wildcard and no explicit match? Then | 
| 417 | 			 * time to quit... | 
| 418 | 			 */ | 
| 419 | 			return false; | 
| 420 | 		} | 
| 421 |  | 
| 422 | 		str++; | 
| 423 | 		pattern++; | 
| 424 | 	} | 
| 425 |  | 
| 426 | 	if (*pattern == '\0') | 
| 427 | 		return true;			/* end of pattern, so declare match */ | 
| 428 |  | 
| 429 | 	/* End of input string.  Do we have matching pattern remaining? */ | 
| 430 | 	while (*pattern == '.' && pattern[1] == '*') | 
| 431 | 		pattern += 2; | 
| 432 | 	if (*pattern == '\0') | 
| 433 | 		return true;			/* end of pattern, so declare match */ | 
| 434 |  | 
| 435 | 	return false; | 
| 436 | } | 
| 437 |  | 
| 438 | /* | 
| 439 |  * Replace all occurrences of a string in a string with a different string. | 
| 440 |  * NOTE: Assumes there is enough room in the target buffer! | 
| 441 |  */ | 
| 442 | void | 
| 443 | replace_string(char *string, const char *replace, const char *replacement) | 
| 444 | { | 
| 445 | 	char	   *ptr; | 
| 446 |  | 
| 447 | 	while ((ptr = strstr(string, replace)) != NULL) | 
| 448 | 	{ | 
| 449 | 		char	   *dup = pg_strdup(string); | 
| 450 |  | 
| 451 | 		strlcpy(string, dup, ptr - string + 1); | 
| 452 | 		strcat(string, replacement); | 
| 453 | 		strcat(string, dup + (ptr - string) + strlen(replace)); | 
| 454 | 		free(dup); | 
| 455 | 	} | 
| 456 | } | 
| 457 |  | 
| 458 | /* | 
| 459 |  * Convert *.source found in the "source" directory, replacing certain tokens | 
| 460 |  * in the file contents with their intended values, and put the resulting files | 
| 461 |  * in the "dest" directory, replacing the ".source" prefix in their names with | 
| 462 |  * the given suffix. | 
| 463 |  */ | 
| 464 | static void | 
| 465 | convert_sourcefiles_in(const char *source_subdir, const char *dest_dir, const char *dest_subdir, const char *suffix) | 
| 466 | { | 
| 467 | 	char		testtablespace[MAXPGPATH]; | 
| 468 | 	char		indir[MAXPGPATH]; | 
| 469 | 	struct stat st; | 
| 470 | 	int			ret; | 
| 471 | 	char	  **name; | 
| 472 | 	char	  **names; | 
| 473 | 	int			count = 0; | 
| 474 |  | 
| 475 | 	snprintf(indir, MAXPGPATH, "%s/%s" , inputdir, source_subdir); | 
| 476 |  | 
| 477 | 	/* Check that indir actually exists and is a directory */ | 
| 478 | 	ret = stat(indir, &st); | 
| 479 | 	if (ret != 0 || !S_ISDIR(st.st_mode)) | 
| 480 | 	{ | 
| 481 | 		/* | 
| 482 | 		 * No warning, to avoid noise in tests that do not have these | 
| 483 | 		 * directories; for example, ecpg, contrib and src/pl. | 
| 484 | 		 */ | 
| 485 | 		return; | 
| 486 | 	} | 
| 487 |  | 
| 488 | 	names = pgfnames(indir); | 
| 489 | 	if (!names) | 
| 490 | 		/* Error logged in pgfnames */ | 
| 491 | 		exit(2); | 
| 492 |  | 
| 493 | 	snprintf(testtablespace, MAXPGPATH, "%s/testtablespace" , outputdir); | 
| 494 |  | 
| 495 | #ifdef WIN32 | 
| 496 |  | 
| 497 | 	/* | 
| 498 | 	 * On Windows only, clean out the test tablespace dir, or create it if it | 
| 499 | 	 * doesn't exist.  On other platforms we expect the Makefile to take care | 
| 500 | 	 * of that.  (We don't migrate that functionality in here because it'd be | 
| 501 | 	 * harder to cope with platform-specific issues such as SELinux.) | 
| 502 | 	 * | 
| 503 | 	 * XXX it would be better if pg_regress.c had nothing at all to do with | 
| 504 | 	 * testtablespace, and this were handled by a .BAT file or similar on | 
| 505 | 	 * Windows.  See pgsql-hackers discussion of 2008-01-18. | 
| 506 | 	 */ | 
| 507 | 	if (directory_exists(testtablespace)) | 
| 508 | 		if (!rmtree(testtablespace, true)) | 
| 509 | 		{ | 
| 510 | 			fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\"\n" ), | 
| 511 | 					progname, testtablespace); | 
| 512 | 			exit(2); | 
| 513 | 		} | 
| 514 | 	make_directory(testtablespace); | 
| 515 | #endif | 
| 516 |  | 
| 517 | 	/* finally loop on each file and do the replacement */ | 
| 518 | 	for (name = names; *name; name++) | 
| 519 | 	{ | 
| 520 | 		char		srcfile[MAXPGPATH]; | 
| 521 | 		char		destfile[MAXPGPATH]; | 
| 522 | 		char		prefix[MAXPGPATH]; | 
| 523 | 		FILE	   *infile, | 
| 524 | 				   *outfile; | 
| 525 | 		char		line[1024]; | 
| 526 |  | 
| 527 | 		/* reject filenames not finishing in ".source" */ | 
| 528 | 		if (strlen(*name) < 8) | 
| 529 | 			continue; | 
| 530 | 		if (strcmp(*name + strlen(*name) - 7, ".source" ) != 0) | 
| 531 | 			continue; | 
| 532 |  | 
| 533 | 		count++; | 
| 534 |  | 
| 535 | 		/* build the full actual paths to open */ | 
| 536 | 		snprintf(prefix, strlen(*name) - 6, "%s" , *name); | 
| 537 | 		snprintf(srcfile, MAXPGPATH, "%s/%s" , indir, *name); | 
| 538 | 		snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s" , dest_dir, dest_subdir, | 
| 539 | 				 prefix, suffix); | 
| 540 |  | 
| 541 | 		infile = fopen(srcfile, "r" ); | 
| 542 | 		if (!infile) | 
| 543 | 		{ | 
| 544 | 			fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n" ), | 
| 545 | 					progname, srcfile, strerror(errno)); | 
| 546 | 			exit(2); | 
| 547 | 		} | 
| 548 | 		outfile = fopen(destfile, "w" ); | 
| 549 | 		if (!outfile) | 
| 550 | 		{ | 
| 551 | 			fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n" ), | 
| 552 | 					progname, destfile, strerror(errno)); | 
| 553 | 			exit(2); | 
| 554 | 		} | 
| 555 | 		while (fgets(line, sizeof(line), infile)) | 
| 556 | 		{ | 
| 557 | 			replace_string(line, "@abs_srcdir@" , inputdir); | 
| 558 | 			replace_string(line, "@abs_builddir@" , outputdir); | 
| 559 | 			replace_string(line, "@testtablespace@" , testtablespace); | 
| 560 | 			replace_string(line, "@libdir@" , dlpath); | 
| 561 | 			replace_string(line, "@DLSUFFIX@" , DLSUFFIX); | 
| 562 | 			fputs(line, outfile); | 
| 563 | 		} | 
| 564 | 		fclose(infile); | 
| 565 | 		fclose(outfile); | 
| 566 | 	} | 
| 567 |  | 
| 568 | 	/* | 
| 569 | 	 * If we didn't process any files, complain because it probably means | 
| 570 | 	 * somebody neglected to pass the needed --inputdir argument. | 
| 571 | 	 */ | 
| 572 | 	if (count <= 0) | 
| 573 | 	{ | 
| 574 | 		fprintf(stderr, _("%s: no *.source files found in \"%s\"\n" ), | 
| 575 | 				progname, indir); | 
| 576 | 		exit(2); | 
| 577 | 	} | 
| 578 |  | 
| 579 | 	pgfnames_cleanup(names); | 
| 580 | } | 
| 581 |  | 
| 582 | /* Create the .sql and .out files from the .source files, if any */ | 
| 583 | static void | 
| 584 | convert_sourcefiles(void) | 
| 585 | { | 
| 586 | 	convert_sourcefiles_in("input" , outputdir, "sql" , "sql" ); | 
| 587 | 	convert_sourcefiles_in("output" , outputdir, "expected" , "out" ); | 
| 588 | } | 
| 589 |  | 
| 590 | /* | 
| 591 |  * Scan resultmap file to find which platform-specific expected files to use. | 
| 592 |  * | 
| 593 |  * The format of each line of the file is | 
| 594 |  *		   testname/hostplatformpattern=substitutefile | 
| 595 |  * where the hostplatformpattern is evaluated per the rules of expr(1), | 
| 596 |  * namely, it is a standard regular expression with an implicit ^ at the start. | 
| 597 |  * (We currently support only a very limited subset of regular expressions, | 
| 598 |  * see string_matches_pattern() above.)  What hostplatformpattern will be | 
| 599 |  * matched against is the config.guess output.  (In the shell-script version, | 
| 600 |  * we also provided an indication of whether gcc or another compiler was in | 
| 601 |  * use, but that facility isn't used anymore.) | 
| 602 |  */ | 
| 603 | static void | 
| 604 | load_resultmap(void) | 
| 605 | { | 
| 606 | 	char		buf[MAXPGPATH]; | 
| 607 | 	FILE	   *f; | 
| 608 |  | 
| 609 | 	/* scan the file ... */ | 
| 610 | 	snprintf(buf, sizeof(buf), "%s/resultmap" , inputdir); | 
| 611 | 	f = fopen(buf, "r" ); | 
| 612 | 	if (!f) | 
| 613 | 	{ | 
| 614 | 		/* OK if it doesn't exist, else complain */ | 
| 615 | 		if (errno == ENOENT) | 
| 616 | 			return; | 
| 617 | 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n" ), | 
| 618 | 				progname, buf, strerror(errno)); | 
| 619 | 		exit(2); | 
| 620 | 	} | 
| 621 |  | 
| 622 | 	while (fgets(buf, sizeof(buf), f)) | 
| 623 | 	{ | 
| 624 | 		char	   *platform; | 
| 625 | 		char	   *file_type; | 
| 626 | 		char	   *expected; | 
| 627 | 		int			i; | 
| 628 |  | 
| 629 | 		/* strip trailing whitespace, especially the newline */ | 
| 630 | 		i = strlen(buf); | 
| 631 | 		while (i > 0 && isspace((unsigned char) buf[i - 1])) | 
| 632 | 			buf[--i] = '\0'; | 
| 633 |  | 
| 634 | 		/* parse out the line fields */ | 
| 635 | 		file_type = strchr(buf, ':'); | 
| 636 | 		if (!file_type) | 
| 637 | 		{ | 
| 638 | 			fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n" ), | 
| 639 | 					buf); | 
| 640 | 			exit(2); | 
| 641 | 		} | 
| 642 | 		*file_type++ = '\0'; | 
| 643 |  | 
| 644 | 		platform = strchr(file_type, ':'); | 
| 645 | 		if (!platform) | 
| 646 | 		{ | 
| 647 | 			fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n" ), | 
| 648 | 					buf); | 
| 649 | 			exit(2); | 
| 650 | 		} | 
| 651 | 		*platform++ = '\0'; | 
| 652 | 		expected = strchr(platform, '='); | 
| 653 | 		if (!expected) | 
| 654 | 		{ | 
| 655 | 			fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n" ), | 
| 656 | 					buf); | 
| 657 | 			exit(2); | 
| 658 | 		} | 
| 659 | 		*expected++ = '\0'; | 
| 660 |  | 
| 661 | 		/* | 
| 662 | 		 * if it's for current platform, save it in resultmap list. Note: by | 
| 663 | 		 * adding at the front of the list, we ensure that in ambiguous cases, | 
| 664 | 		 * the last match in the resultmap file is used. This mimics the | 
| 665 | 		 * behavior of the old shell script. | 
| 666 | 		 */ | 
| 667 | 		if (string_matches_pattern(host_platform, platform)) | 
| 668 | 		{ | 
| 669 | 			_resultmap *entry = pg_malloc(sizeof(_resultmap)); | 
| 670 |  | 
| 671 | 			entry->test = pg_strdup(buf); | 
| 672 | 			entry->type = pg_strdup(file_type); | 
| 673 | 			entry->resultfile = pg_strdup(expected); | 
| 674 | 			entry->next = resultmap; | 
| 675 | 			resultmap = entry; | 
| 676 | 		} | 
| 677 | 	} | 
| 678 | 	fclose(f); | 
| 679 | } | 
| 680 |  | 
| 681 | /* | 
| 682 |  * Check in resultmap if we should be looking at a different file | 
| 683 |  */ | 
| 684 | static | 
| 685 | const char * | 
| 686 | get_expectfile(const char *testname, const char *file) | 
| 687 | { | 
| 688 | 	char	   *file_type; | 
| 689 | 	_resultmap *rm; | 
| 690 |  | 
| 691 | 	/* | 
| 692 | 	 * Determine the file type from the file name. This is just what is | 
| 693 | 	 * following the last dot in the file name. | 
| 694 | 	 */ | 
| 695 | 	if (!file || !(file_type = strrchr(file, '.'))) | 
| 696 | 		return NULL; | 
| 697 |  | 
| 698 | 	file_type++; | 
| 699 |  | 
| 700 | 	for (rm = resultmap; rm != NULL; rm = rm->next) | 
| 701 | 	{ | 
| 702 | 		if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0) | 
| 703 | 		{ | 
| 704 | 			return rm->resultfile; | 
| 705 | 		} | 
| 706 | 	} | 
| 707 |  | 
| 708 | 	return NULL; | 
| 709 | } | 
| 710 |  | 
| 711 | /* | 
| 712 |  * Handy subroutine for setting an environment variable "var" to "val" | 
| 713 |  */ | 
| 714 | static void | 
| 715 | doputenv(const char *var, const char *val) | 
| 716 | { | 
| 717 | 	char	   *s; | 
| 718 |  | 
| 719 | 	s = psprintf("%s=%s" , var, val); | 
| 720 | 	putenv(s); | 
| 721 | } | 
| 722 |  | 
| 723 | /* | 
| 724 |  * Prepare environment variables for running regression tests | 
| 725 |  */ | 
| 726 | static void | 
| 727 | initialize_environment(void) | 
| 728 | { | 
| 729 | 	putenv("PGAPPNAME=pg_regress" ); | 
| 730 |  | 
| 731 | 	if (nolocale) | 
| 732 | 	{ | 
| 733 | 		/* | 
| 734 | 		 * Clear out any non-C locale settings | 
| 735 | 		 */ | 
| 736 | 		unsetenv("LC_COLLATE" ); | 
| 737 | 		unsetenv("LC_CTYPE" ); | 
| 738 | 		unsetenv("LC_MONETARY" ); | 
| 739 | 		unsetenv("LC_NUMERIC" ); | 
| 740 | 		unsetenv("LC_TIME" ); | 
| 741 | 		unsetenv("LANG" ); | 
| 742 |  | 
| 743 | 		/* | 
| 744 | 		 * Most platforms have adopted the POSIX locale as their | 
| 745 | 		 * implementation-defined default locale.  Exceptions include native | 
| 746 | 		 * Windows, macOS with --enable-nls, and Cygwin with --enable-nls. | 
| 747 | 		 * (Use of --enable-nls matters because libintl replaces setlocale().) | 
| 748 | 		 * Also, PostgreSQL does not support macOS with locale environment | 
| 749 | 		 * variables unset; see PostmasterMain(). | 
| 750 | 		 */ | 
| 751 | #if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__) | 
| 752 | 		putenv("LANG=C" ); | 
| 753 | #endif | 
| 754 | 	} | 
| 755 |  | 
| 756 | 	/* | 
| 757 | 	 * Set translation-related settings to English; otherwise psql will | 
| 758 | 	 * produce translated messages and produce diffs.  (XXX If we ever support | 
| 759 | 	 * translation of pg_regress, this needs to be moved elsewhere, where psql | 
| 760 | 	 * is actually called.) | 
| 761 | 	 */ | 
| 762 | 	unsetenv("LANGUAGE" ); | 
| 763 | 	unsetenv("LC_ALL" ); | 
| 764 | 	putenv("LC_MESSAGES=C" ); | 
| 765 |  | 
| 766 | 	/* | 
| 767 | 	 * Set encoding as requested | 
| 768 | 	 */ | 
| 769 | 	if (encoding) | 
| 770 | 		doputenv("PGCLIENTENCODING" , encoding); | 
| 771 | 	else | 
| 772 | 		unsetenv("PGCLIENTENCODING" ); | 
| 773 |  | 
| 774 | 	/* | 
| 775 | 	 * Set timezone and datestyle for datetime-related tests | 
| 776 | 	 */ | 
| 777 | 	putenv("PGTZ=PST8PDT" ); | 
| 778 | 	putenv("PGDATESTYLE=Postgres, MDY" ); | 
| 779 |  | 
| 780 | 	/* | 
| 781 | 	 * Likewise set intervalstyle to ensure consistent results.  This is a bit | 
| 782 | 	 * more painful because we must use PGOPTIONS, and we want to preserve the | 
| 783 | 	 * user's ability to set other variables through that. | 
| 784 | 	 */ | 
| 785 | 	{ | 
| 786 | 		const char *my_pgoptions = "-c intervalstyle=postgres_verbose" ; | 
| 787 | 		const char *old_pgoptions = getenv("PGOPTIONS" ); | 
| 788 | 		char	   *new_pgoptions; | 
| 789 |  | 
| 790 | 		if (!old_pgoptions) | 
| 791 | 			old_pgoptions = "" ; | 
| 792 | 		new_pgoptions = psprintf("PGOPTIONS=%s %s" , | 
| 793 | 								 old_pgoptions, my_pgoptions); | 
| 794 | 		putenv(new_pgoptions); | 
| 795 | 	} | 
| 796 |  | 
| 797 | 	if (temp_instance) | 
| 798 | 	{ | 
| 799 | 		/* | 
| 800 | 		 * Clear out any environment vars that might cause psql to connect to | 
| 801 | 		 * the wrong postmaster, or otherwise behave in nondefault ways. (Note | 
| 802 | 		 * we also use psql's -X switch consistently, so that ~/.psqlrc files | 
| 803 | 		 * won't mess things up.)  Also, set PGPORT to the temp port, and set | 
| 804 | 		 * PGHOST depending on whether we are using TCP or Unix sockets. | 
| 805 | 		 */ | 
| 806 | 		unsetenv("PGDATABASE" ); | 
| 807 | 		unsetenv("PGUSER" ); | 
| 808 | 		unsetenv("PGSERVICE" ); | 
| 809 | 		unsetenv("PGSSLMODE" ); | 
| 810 | 		unsetenv("PGREQUIRESSL" ); | 
| 811 | 		unsetenv("PGCONNECT_TIMEOUT" ); | 
| 812 | 		unsetenv("PGDATA" ); | 
| 813 | #ifdef HAVE_UNIX_SOCKETS | 
| 814 | 		if (hostname != NULL) | 
| 815 | 			doputenv("PGHOST" , hostname); | 
| 816 | 		else | 
| 817 | 		{ | 
| 818 | 			sockdir = getenv("PG_REGRESS_SOCK_DIR" ); | 
| 819 | 			if (!sockdir) | 
| 820 | 				sockdir = make_temp_sockdir(); | 
| 821 | 			doputenv("PGHOST" , sockdir); | 
| 822 | 		} | 
| 823 | #else | 
| 824 | 		Assert(hostname != NULL); | 
| 825 | 		doputenv("PGHOST" , hostname); | 
| 826 | #endif | 
| 827 | 		unsetenv("PGHOSTADDR" ); | 
| 828 | 		if (port != -1) | 
| 829 | 		{ | 
| 830 | 			char		s[16]; | 
| 831 |  | 
| 832 | 			sprintf(s, "%d" , port); | 
| 833 | 			doputenv("PGPORT" , s); | 
| 834 | 		} | 
| 835 | 	} | 
| 836 | 	else | 
| 837 | 	{ | 
| 838 | 		const char *pghost; | 
| 839 | 		const char *pgport; | 
| 840 |  | 
| 841 | 		/* | 
| 842 | 		 * When testing an existing install, we honor existing environment | 
| 843 | 		 * variables, except if they're overridden by command line options. | 
| 844 | 		 */ | 
| 845 | 		if (hostname != NULL) | 
| 846 | 		{ | 
| 847 | 			doputenv("PGHOST" , hostname); | 
| 848 | 			unsetenv("PGHOSTADDR" ); | 
| 849 | 		} | 
| 850 | 		if (port != -1) | 
| 851 | 		{ | 
| 852 | 			char		s[16]; | 
| 853 |  | 
| 854 | 			sprintf(s, "%d" , port); | 
| 855 | 			doputenv("PGPORT" , s); | 
| 856 | 		} | 
| 857 | 		if (user != NULL) | 
| 858 | 			doputenv("PGUSER" , user); | 
| 859 |  | 
| 860 | 		/* | 
| 861 | 		 * Report what we're connecting to | 
| 862 | 		 */ | 
| 863 | 		pghost = getenv("PGHOST" ); | 
| 864 | 		pgport = getenv("PGPORT" ); | 
| 865 | #ifndef HAVE_UNIX_SOCKETS | 
| 866 | 		if (!pghost) | 
| 867 | 			pghost = "localhost" ; | 
| 868 | #endif | 
| 869 |  | 
| 870 | 		if (pghost && pgport) | 
| 871 | 			printf(_("(using postmaster on %s, port %s)\n" ), pghost, pgport); | 
| 872 | 		if (pghost && !pgport) | 
| 873 | 			printf(_("(using postmaster on %s, default port)\n" ), pghost); | 
| 874 | 		if (!pghost && pgport) | 
| 875 | 			printf(_("(using postmaster on Unix socket, port %s)\n" ), pgport); | 
| 876 | 		if (!pghost && !pgport) | 
| 877 | 			printf(_("(using postmaster on Unix socket, default port)\n" )); | 
| 878 | 	} | 
| 879 |  | 
| 880 | 	convert_sourcefiles(); | 
| 881 | 	load_resultmap(); | 
| 882 | } | 
| 883 |  | 
| 884 | #ifdef ENABLE_SSPI | 
| 885 |  | 
| 886 | /* support for config_sspi_auth() */ | 
| 887 | static const char * | 
| 888 | fmtHba(const char *raw) | 
| 889 | { | 
| 890 | 	static char *ret; | 
| 891 | 	const char *rp; | 
| 892 | 	char	   *wp; | 
| 893 |  | 
| 894 | 	wp = ret = realloc(ret, 3 + strlen(raw) * 2); | 
| 895 |  | 
| 896 | 	*wp++ = '"'; | 
| 897 | 	for (rp = raw; *rp; rp++) | 
| 898 | 	{ | 
| 899 | 		if (*rp == '"') | 
| 900 | 			*wp++ = '"'; | 
| 901 | 		*wp++ = *rp; | 
| 902 | 	} | 
| 903 | 	*wp++ = '"'; | 
| 904 | 	*wp++ = '\0'; | 
| 905 |  | 
| 906 | 	return ret; | 
| 907 | } | 
| 908 |  | 
| 909 | /* | 
| 910 |  * Get account and domain/realm names for the current user.  This is based on | 
| 911 |  * pg_SSPI_recvauth().  The returned strings use static storage. | 
| 912 |  */ | 
| 913 | static void | 
| 914 | current_windows_user(const char **acct, const char **dom) | 
| 915 | { | 
| 916 | 	static char accountname[MAXPGPATH]; | 
| 917 | 	static char domainname[MAXPGPATH]; | 
| 918 | 	HANDLE		token; | 
| 919 | 	TOKEN_USER *tokenuser; | 
| 920 | 	DWORD		retlen; | 
| 921 | 	DWORD		accountnamesize = sizeof(accountname); | 
| 922 | 	DWORD		domainnamesize = sizeof(domainname); | 
| 923 | 	SID_NAME_USE accountnameuse; | 
| 924 |  | 
| 925 | 	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token)) | 
| 926 | 	{ | 
| 927 | 		fprintf(stderr, | 
| 928 | 				_("%s: could not open process token: error code %lu\n" ), | 
| 929 | 				progname, GetLastError()); | 
| 930 | 		exit(2); | 
| 931 | 	} | 
| 932 |  | 
| 933 | 	if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122) | 
| 934 | 	{ | 
| 935 | 		fprintf(stderr, | 
| 936 | 				_("%s: could not get token information buffer size: error code %lu\n" ), | 
| 937 | 				progname, GetLastError()); | 
| 938 | 		exit(2); | 
| 939 | 	} | 
| 940 | 	tokenuser = pg_malloc(retlen); | 
| 941 | 	if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen)) | 
| 942 | 	{ | 
| 943 | 		fprintf(stderr, | 
| 944 | 				_("%s: could not get token information: error code %lu\n" ), | 
| 945 | 				progname, GetLastError()); | 
| 946 | 		exit(2); | 
| 947 | 	} | 
| 948 |  | 
| 949 | 	if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize, | 
| 950 | 						  domainname, &domainnamesize, &accountnameuse)) | 
| 951 | 	{ | 
| 952 | 		fprintf(stderr, | 
| 953 | 				_("%s: could not look up account SID: error code %lu\n" ), | 
| 954 | 				progname, GetLastError()); | 
| 955 | 		exit(2); | 
| 956 | 	} | 
| 957 |  | 
| 958 | 	free(tokenuser); | 
| 959 |  | 
| 960 | 	*acct = accountname; | 
| 961 | 	*dom = domainname; | 
| 962 | } | 
| 963 |  | 
| 964 | /* | 
| 965 |  * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication.  Permit | 
| 966 |  * the current OS user to authenticate as the bootstrap superuser and as any | 
| 967 |  * user named in a --create-role option. | 
| 968 |  * | 
| 969 |  * In --config-auth mode, the --user switch can be used to specify the | 
| 970 |  * bootstrap superuser's name, otherwise we assume it is the default. | 
| 971 |  */ | 
| 972 | static void | 
| 973 | config_sspi_auth(const char *pgdata, const char *superuser_name) | 
| 974 | { | 
| 975 | 	const char *accountname, | 
| 976 | 			   *domainname; | 
| 977 | 	char	   *errstr; | 
| 978 | 	bool		have_ipv6; | 
| 979 | 	char		fname[MAXPGPATH]; | 
| 980 | 	int			res; | 
| 981 | 	FILE	   *hba, | 
| 982 | 			   *ident; | 
| 983 | 	_stringlist *sl; | 
| 984 |  | 
| 985 | 	/* Find out the name of the current OS user */ | 
| 986 | 	current_windows_user(&accountname, &domainname); | 
| 987 |  | 
| 988 | 	/* Determine the bootstrap superuser's name */ | 
| 989 | 	if (superuser_name == NULL) | 
| 990 | 	{ | 
| 991 | 		/* | 
| 992 | 		 * Compute the default superuser name the same way initdb does. | 
| 993 | 		 * | 
| 994 | 		 * It's possible that this result always matches "accountname", the | 
| 995 | 		 * value SSPI authentication discovers.  But the underlying system | 
| 996 | 		 * functions do not clearly guarantee that. | 
| 997 | 		 */ | 
| 998 | 		superuser_name = get_user_name(&errstr); | 
| 999 | 		if (superuser_name == NULL) | 
| 1000 | 		{ | 
| 1001 | 			fprintf(stderr, "%s: %s\n" , progname, errstr); | 
| 1002 | 			exit(2); | 
| 1003 | 		} | 
| 1004 | 	} | 
| 1005 |  | 
| 1006 | 	/* | 
| 1007 | 	 * Like initdb.c:setup_config(), determine whether the platform recognizes | 
| 1008 | 	 * ::1 (IPv6 loopback) as a numeric host address string. | 
| 1009 | 	 */ | 
| 1010 | 	{ | 
| 1011 | 		struct addrinfo *gai_result; | 
| 1012 | 		struct addrinfo hints; | 
| 1013 | 		WSADATA		wsaData; | 
| 1014 |  | 
| 1015 | 		hints.ai_flags = AI_NUMERICHOST; | 
| 1016 | 		hints.ai_family = AF_UNSPEC; | 
| 1017 | 		hints.ai_socktype = 0; | 
| 1018 | 		hints.ai_protocol = 0; | 
| 1019 | 		hints.ai_addrlen = 0; | 
| 1020 | 		hints.ai_canonname = NULL; | 
| 1021 | 		hints.ai_addr = NULL; | 
| 1022 | 		hints.ai_next = NULL; | 
| 1023 |  | 
| 1024 | 		have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 && | 
| 1025 | 					 getaddrinfo("::1" , NULL, &hints, &gai_result) == 0); | 
| 1026 | 	} | 
| 1027 |  | 
| 1028 | 	/* Check a Write outcome and report any error. */ | 
| 1029 | #define CW(cond)	\ | 
| 1030 | 	do { \ | 
| 1031 | 		if (!(cond)) \ | 
| 1032 | 		{ \ | 
| 1033 | 			fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \ | 
| 1034 | 					progname, fname, strerror(errno)); \ | 
| 1035 | 			exit(2); \ | 
| 1036 | 		} \ | 
| 1037 | 	} while (0) | 
| 1038 |  | 
| 1039 | 	res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf" , pgdata); | 
| 1040 | 	if (res < 0 || res >= sizeof(fname)) | 
| 1041 | 	{ | 
| 1042 | 		/* | 
| 1043 | 		 * Truncating this name is a fatal error, because we must not fail to | 
| 1044 | 		 * overwrite an original trust-authentication pg_hba.conf. | 
| 1045 | 		 */ | 
| 1046 | 		fprintf(stderr, _("%s: directory name too long\n" ), progname); | 
| 1047 | 		exit(2); | 
| 1048 | 	} | 
| 1049 | 	hba = fopen(fname, "w" ); | 
| 1050 | 	if (hba == NULL) | 
| 1051 | 	{ | 
| 1052 | 		fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n" ), | 
| 1053 | 				progname, fname, strerror(errno)); | 
| 1054 | 		exit(2); | 
| 1055 | 	} | 
| 1056 | 	CW(fputs("# Configuration written by config_sspi_auth()\n" , hba) >= 0); | 
| 1057 | 	CW(fputs("host all all 127.0.0.1/32  sspi include_realm=1 map=regress\n" , | 
| 1058 | 			 hba) >= 0); | 
| 1059 | 	if (have_ipv6) | 
| 1060 | 		CW(fputs("host all all ::1/128  sspi include_realm=1 map=regress\n" , | 
| 1061 | 				 hba) >= 0); | 
| 1062 | 	CW(fclose(hba) == 0); | 
| 1063 |  | 
| 1064 | 	snprintf(fname, sizeof(fname), "%s/pg_ident.conf" , pgdata); | 
| 1065 | 	ident = fopen(fname, "w" ); | 
| 1066 | 	if (ident == NULL) | 
| 1067 | 	{ | 
| 1068 | 		fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n" ), | 
| 1069 | 				progname, fname, strerror(errno)); | 
| 1070 | 		exit(2); | 
| 1071 | 	} | 
| 1072 | 	CW(fputs("# Configuration written by config_sspi_auth()\n" , ident) >= 0); | 
| 1073 |  | 
| 1074 | 	/* | 
| 1075 | 	 * Double-quote for the benefit of account names containing whitespace or | 
| 1076 | 	 * '#'.  Windows forbids the double-quote character itself, so don't | 
| 1077 | 	 * bother escaping embedded double-quote characters. | 
| 1078 | 	 */ | 
| 1079 | 	CW(fprintf(ident, "regress  \"%s@%s\"  %s\n" , | 
| 1080 | 			   accountname, domainname, fmtHba(superuser_name)) >= 0); | 
| 1081 | 	for (sl = extraroles; sl; sl = sl->next) | 
| 1082 | 		CW(fprintf(ident, "regress  \"%s@%s\"  %s\n" , | 
| 1083 | 				   accountname, domainname, fmtHba(sl->str)) >= 0); | 
| 1084 | 	CW(fclose(ident) == 0); | 
| 1085 | } | 
| 1086 |  | 
| 1087 | #endif							/* ENABLE_SSPI */ | 
| 1088 |  | 
| 1089 | /* | 
| 1090 |  * Issue a command via psql, connecting to the specified database | 
| 1091 |  * | 
| 1092 |  * Since we use system(), this doesn't return until the operation finishes | 
| 1093 |  */ | 
| 1094 | static void | 
| 1095 | psql_command(const char *database, const char *query,...) | 
| 1096 | { | 
| 1097 | 	char		query_formatted[1024]; | 
| 1098 | 	char		query_escaped[2048]; | 
| 1099 | 	char		psql_cmd[MAXPGPATH + 2048]; | 
| 1100 | 	va_list		args; | 
| 1101 | 	char	   *s; | 
| 1102 | 	char	   *d; | 
| 1103 |  | 
| 1104 | 	/* Generate the query with insertion of sprintf arguments */ | 
| 1105 | 	va_start(args, query); | 
| 1106 | 	vsnprintf(query_formatted, sizeof(query_formatted), query, args); | 
| 1107 | 	va_end(args); | 
| 1108 |  | 
| 1109 | 	/* Now escape any shell double-quote metacharacters */ | 
| 1110 | 	d = query_escaped; | 
| 1111 | 	for (s = query_formatted; *s; s++) | 
| 1112 | 	{ | 
| 1113 | 		if (strchr("\\\"$`" , *s)) | 
| 1114 | 			*d++ = '\\'; | 
| 1115 | 		*d++ = *s; | 
| 1116 | 	} | 
| 1117 | 	*d = '\0'; | 
| 1118 |  | 
| 1119 | 	/* And now we can build and execute the shell command */ | 
| 1120 | 	snprintf(psql_cmd, sizeof(psql_cmd), | 
| 1121 | 			 "\"%s%spsql\" -X -c \"%s\" \"%s\"" , | 
| 1122 | 			 bindir ? bindir : "" , | 
| 1123 | 			 bindir ? "/"  : "" , | 
| 1124 | 			 query_escaped, | 
| 1125 | 			 database); | 
| 1126 |  | 
| 1127 | 	if (system(psql_cmd) != 0) | 
| 1128 | 	{ | 
| 1129 | 		/* psql probably already reported the error */ | 
| 1130 | 		fprintf(stderr, _("command failed: %s\n" ), psql_cmd); | 
| 1131 | 		exit(2); | 
| 1132 | 	} | 
| 1133 | } | 
| 1134 |  | 
| 1135 | /* | 
| 1136 |  * Spawn a process to execute the given shell command; don't wait for it | 
| 1137 |  * | 
| 1138 |  * Returns the process ID (or HANDLE) so we can wait for it later | 
| 1139 |  */ | 
| 1140 | PID_TYPE | 
| 1141 | spawn_process(const char *cmdline) | 
| 1142 | { | 
| 1143 | #ifndef WIN32 | 
| 1144 | 	pid_t		pid; | 
| 1145 |  | 
| 1146 | 	/* | 
| 1147 | 	 * Must flush I/O buffers before fork.  Ideally we'd use fflush(NULL) here | 
| 1148 | 	 * ... does anyone still care about systems where that doesn't work? | 
| 1149 | 	 */ | 
| 1150 | 	fflush(stdout); | 
| 1151 | 	fflush(stderr); | 
| 1152 | 	if (logfile) | 
| 1153 | 		fflush(logfile); | 
| 1154 |  | 
| 1155 | 	pid = fork(); | 
| 1156 | 	if (pid == -1) | 
| 1157 | 	{ | 
| 1158 | 		fprintf(stderr, _("%s: could not fork: %s\n" ), | 
| 1159 | 				progname, strerror(errno)); | 
| 1160 | 		exit(2); | 
| 1161 | 	} | 
| 1162 | 	if (pid == 0) | 
| 1163 | 	{ | 
| 1164 | 		/* | 
| 1165 | 		 * In child | 
| 1166 | 		 * | 
| 1167 | 		 * Instead of using system(), exec the shell directly, and tell it to | 
| 1168 | 		 * "exec" the command too.  This saves two useless processes per | 
| 1169 | 		 * parallel test case. | 
| 1170 | 		 */ | 
| 1171 | 		char	   *cmdline2; | 
| 1172 |  | 
| 1173 | 		cmdline2 = psprintf("exec %s" , cmdline); | 
| 1174 | 		execl(shellprog, shellprog, "-c" , cmdline2, (char *) NULL); | 
| 1175 | 		fprintf(stderr, _("%s: could not exec \"%s\": %s\n" ), | 
| 1176 | 				progname, shellprog, strerror(errno)); | 
| 1177 | 		_exit(1);				/* not exit() here... */ | 
| 1178 | 	} | 
| 1179 | 	/* in parent */ | 
| 1180 | 	return pid; | 
| 1181 | #else | 
| 1182 | 	PROCESS_INFORMATION pi; | 
| 1183 | 	char	   *cmdline2; | 
| 1184 | 	HANDLE		restrictedToken; | 
| 1185 |  | 
| 1186 | 	memset(&pi, 0, sizeof(pi)); | 
| 1187 | 	cmdline2 = psprintf("cmd /c \"%s\"" , cmdline); | 
| 1188 |  | 
| 1189 | 	if ((restrictedToken = | 
| 1190 | 		 CreateRestrictedProcess(cmdline2, &pi)) == 0) | 
| 1191 | 		exit(2); | 
| 1192 |  | 
| 1193 | 	CloseHandle(pi.hThread); | 
| 1194 | 	return pi.hProcess; | 
| 1195 | #endif | 
| 1196 | } | 
| 1197 |  | 
| 1198 | /* | 
| 1199 |  * Count bytes in file | 
| 1200 |  */ | 
| 1201 | static long | 
| 1202 | file_size(const char *file) | 
| 1203 | { | 
| 1204 | 	long		r; | 
| 1205 | 	FILE	   *f = fopen(file, "r" ); | 
| 1206 |  | 
| 1207 | 	if (!f) | 
| 1208 | 	{ | 
| 1209 | 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n" ), | 
| 1210 | 				progname, file, strerror(errno)); | 
| 1211 | 		return -1; | 
| 1212 | 	} | 
| 1213 | 	fseek(f, 0, SEEK_END); | 
| 1214 | 	r = ftell(f); | 
| 1215 | 	fclose(f); | 
| 1216 | 	return r; | 
| 1217 | } | 
| 1218 |  | 
| 1219 | /* | 
| 1220 |  * Count lines in file | 
| 1221 |  */ | 
| 1222 | static int | 
| 1223 | file_line_count(const char *file) | 
| 1224 | { | 
| 1225 | 	int			c; | 
| 1226 | 	int			l = 0; | 
| 1227 | 	FILE	   *f = fopen(file, "r" ); | 
| 1228 |  | 
| 1229 | 	if (!f) | 
| 1230 | 	{ | 
| 1231 | 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n" ), | 
| 1232 | 				progname, file, strerror(errno)); | 
| 1233 | 		return -1; | 
| 1234 | 	} | 
| 1235 | 	while ((c = fgetc(f)) != EOF) | 
| 1236 | 	{ | 
| 1237 | 		if (c == '\n') | 
| 1238 | 			l++; | 
| 1239 | 	} | 
| 1240 | 	fclose(f); | 
| 1241 | 	return l; | 
| 1242 | } | 
| 1243 |  | 
| 1244 | bool | 
| 1245 | file_exists(const char *file) | 
| 1246 | { | 
| 1247 | 	FILE	   *f = fopen(file, "r" ); | 
| 1248 |  | 
| 1249 | 	if (!f) | 
| 1250 | 		return false; | 
| 1251 | 	fclose(f); | 
| 1252 | 	return true; | 
| 1253 | } | 
| 1254 |  | 
| 1255 | static bool | 
| 1256 | directory_exists(const char *dir) | 
| 1257 | { | 
| 1258 | 	struct stat st; | 
| 1259 |  | 
| 1260 | 	if (stat(dir, &st) != 0) | 
| 1261 | 		return false; | 
| 1262 | 	if (S_ISDIR(st.st_mode)) | 
| 1263 | 		return true; | 
| 1264 | 	return false; | 
| 1265 | } | 
| 1266 |  | 
| 1267 | /* Create a directory */ | 
| 1268 | static void | 
| 1269 | make_directory(const char *dir) | 
| 1270 | { | 
| 1271 | 	if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0) | 
| 1272 | 	{ | 
| 1273 | 		fprintf(stderr, _("%s: could not create directory \"%s\": %s\n" ), | 
| 1274 | 				progname, dir, strerror(errno)); | 
| 1275 | 		exit(2); | 
| 1276 | 	} | 
| 1277 | } | 
| 1278 |  | 
| 1279 | /* | 
| 1280 |  * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9 | 
| 1281 |  */ | 
| 1282 | static char * | 
| 1283 | get_alternative_expectfile(const char *expectfile, int i) | 
| 1284 | { | 
| 1285 | 	char	   *last_dot; | 
| 1286 | 	int			ssize = strlen(expectfile) + 2 + 1; | 
| 1287 | 	char	   *tmp; | 
| 1288 | 	char	   *s; | 
| 1289 |  | 
| 1290 | 	if (!(tmp = (char *) malloc(ssize))) | 
| 1291 | 		return NULL; | 
| 1292 |  | 
| 1293 | 	if (!(s = (char *) malloc(ssize))) | 
| 1294 | 	{ | 
| 1295 | 		free(tmp); | 
| 1296 | 		return NULL; | 
| 1297 | 	} | 
| 1298 |  | 
| 1299 | 	strcpy(tmp, expectfile); | 
| 1300 | 	last_dot = strrchr(tmp, '.'); | 
| 1301 | 	if (!last_dot) | 
| 1302 | 	{ | 
| 1303 | 		free(tmp); | 
| 1304 | 		free(s); | 
| 1305 | 		return NULL; | 
| 1306 | 	} | 
| 1307 | 	*last_dot = '\0'; | 
| 1308 | 	snprintf(s, ssize, "%s_%d.%s" , tmp, i, last_dot + 1); | 
| 1309 | 	free(tmp); | 
| 1310 | 	return s; | 
| 1311 | } | 
| 1312 |  | 
| 1313 | /* | 
| 1314 |  * Run a "diff" command and also check that it didn't crash | 
| 1315 |  */ | 
| 1316 | static int | 
| 1317 | run_diff(const char *cmd, const char *filename) | 
| 1318 | { | 
| 1319 | 	int			r; | 
| 1320 |  | 
| 1321 | 	r = system(cmd); | 
| 1322 | 	if (!WIFEXITED(r) || WEXITSTATUS(r) > 1) | 
| 1323 | 	{ | 
| 1324 | 		fprintf(stderr, _("diff command failed with status %d: %s\n" ), r, cmd); | 
| 1325 | 		exit(2); | 
| 1326 | 	} | 
| 1327 | #ifdef WIN32 | 
| 1328 |  | 
| 1329 | 	/* | 
| 1330 | 	 * On WIN32, if the 'diff' command cannot be found, system() returns 1, | 
| 1331 | 	 * but produces nothing to stdout, so we check for that here. | 
| 1332 | 	 */ | 
| 1333 | 	if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0) | 
| 1334 | 	{ | 
| 1335 | 		fprintf(stderr, _("diff command not found: %s\n" ), cmd); | 
| 1336 | 		exit(2); | 
| 1337 | 	} | 
| 1338 | #endif | 
| 1339 |  | 
| 1340 | 	return WEXITSTATUS(r); | 
| 1341 | } | 
| 1342 |  | 
| 1343 | /* | 
| 1344 |  * Check the actual result file for the given test against expected results | 
| 1345 |  * | 
| 1346 |  * Returns true if different (failure), false if correct match found. | 
| 1347 |  * In the true case, the diff is appended to the diffs file. | 
| 1348 |  */ | 
| 1349 | static bool | 
| 1350 | results_differ(const char *testname, const char *resultsfile, const char *default_expectfile) | 
| 1351 | { | 
| 1352 | 	char		expectfile[MAXPGPATH]; | 
| 1353 | 	char		diff[MAXPGPATH]; | 
| 1354 | 	char		cmd[MAXPGPATH * 3]; | 
| 1355 | 	char		best_expect_file[MAXPGPATH]; | 
| 1356 | 	FILE	   *difffile; | 
| 1357 | 	int			best_line_count; | 
| 1358 | 	int			i; | 
| 1359 | 	int			l; | 
| 1360 | 	const char *platform_expectfile; | 
| 1361 |  | 
| 1362 | 	/* | 
| 1363 | 	 * We can pass either the resultsfile or the expectfile, they should have | 
| 1364 | 	 * the same type (filename.type) anyway. | 
| 1365 | 	 */ | 
| 1366 | 	platform_expectfile = get_expectfile(testname, resultsfile); | 
| 1367 |  | 
| 1368 | 	strlcpy(expectfile, default_expectfile, sizeof(expectfile)); | 
| 1369 | 	if (platform_expectfile) | 
| 1370 | 	{ | 
| 1371 | 		/* | 
| 1372 | 		 * Replace everything after the last slash in expectfile with what the | 
| 1373 | 		 * platform_expectfile contains. | 
| 1374 | 		 */ | 
| 1375 | 		char	   *p = strrchr(expectfile, '/'); | 
| 1376 |  | 
| 1377 | 		if (p) | 
| 1378 | 			strcpy(++p, platform_expectfile); | 
| 1379 | 	} | 
| 1380 |  | 
| 1381 | 	/* Name to use for temporary diff file */ | 
| 1382 | 	snprintf(diff, sizeof(diff), "%s.diff" , resultsfile); | 
| 1383 |  | 
| 1384 | 	/* OK, run the diff */ | 
| 1385 | 	snprintf(cmd, sizeof(cmd), | 
| 1386 | 			 "diff %s \"%s\" \"%s\" > \"%s\"" , | 
| 1387 | 			 basic_diff_opts, expectfile, resultsfile, diff); | 
| 1388 |  | 
| 1389 | 	/* Is the diff file empty? */ | 
| 1390 | 	if (run_diff(cmd, diff) == 0) | 
| 1391 | 	{ | 
| 1392 | 		unlink(diff); | 
| 1393 | 		return false; | 
| 1394 | 	} | 
| 1395 |  | 
| 1396 | 	/* There may be secondary comparison files that match better */ | 
| 1397 | 	best_line_count = file_line_count(diff); | 
| 1398 | 	strcpy(best_expect_file, expectfile); | 
| 1399 |  | 
| 1400 | 	for (i = 0; i <= 9; i++) | 
| 1401 | 	{ | 
| 1402 | 		char	   *alt_expectfile; | 
| 1403 |  | 
| 1404 | 		alt_expectfile = get_alternative_expectfile(expectfile, i); | 
| 1405 | 		if (!alt_expectfile) | 
| 1406 | 		{ | 
| 1407 | 			fprintf(stderr, _("Unable to check secondary comparison files: %s\n" ), | 
| 1408 | 					strerror(errno)); | 
| 1409 | 			exit(2); | 
| 1410 | 		} | 
| 1411 |  | 
| 1412 | 		if (!file_exists(alt_expectfile)) | 
| 1413 | 		{ | 
| 1414 | 			free(alt_expectfile); | 
| 1415 | 			continue; | 
| 1416 | 		} | 
| 1417 |  | 
| 1418 | 		snprintf(cmd, sizeof(cmd), | 
| 1419 | 				 "diff %s \"%s\" \"%s\" > \"%s\"" , | 
| 1420 | 				 basic_diff_opts, alt_expectfile, resultsfile, diff); | 
| 1421 |  | 
| 1422 | 		if (run_diff(cmd, diff) == 0) | 
| 1423 | 		{ | 
| 1424 | 			unlink(diff); | 
| 1425 | 			free(alt_expectfile); | 
| 1426 | 			return false; | 
| 1427 | 		} | 
| 1428 |  | 
| 1429 | 		l = file_line_count(diff); | 
| 1430 | 		if (l < best_line_count) | 
| 1431 | 		{ | 
| 1432 | 			/* This diff was a better match than the last one */ | 
| 1433 | 			best_line_count = l; | 
| 1434 | 			strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file)); | 
| 1435 | 		} | 
| 1436 | 		free(alt_expectfile); | 
| 1437 | 	} | 
| 1438 |  | 
| 1439 | 	/* | 
| 1440 | 	 * fall back on the canonical results file if we haven't tried it yet and | 
| 1441 | 	 * haven't found a complete match yet. | 
| 1442 | 	 */ | 
| 1443 |  | 
| 1444 | 	if (platform_expectfile) | 
| 1445 | 	{ | 
| 1446 | 		snprintf(cmd, sizeof(cmd), | 
| 1447 | 				 "diff %s \"%s\" \"%s\" > \"%s\"" , | 
| 1448 | 				 basic_diff_opts, default_expectfile, resultsfile, diff); | 
| 1449 |  | 
| 1450 | 		if (run_diff(cmd, diff) == 0) | 
| 1451 | 		{ | 
| 1452 | 			/* No diff = no changes = good */ | 
| 1453 | 			unlink(diff); | 
| 1454 | 			return false; | 
| 1455 | 		} | 
| 1456 |  | 
| 1457 | 		l = file_line_count(diff); | 
| 1458 | 		if (l < best_line_count) | 
| 1459 | 		{ | 
| 1460 | 			/* This diff was a better match than the last one */ | 
| 1461 | 			best_line_count = l; | 
| 1462 | 			strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file)); | 
| 1463 | 		} | 
| 1464 | 	} | 
| 1465 |  | 
| 1466 | 	/* | 
| 1467 | 	 * Use the best comparison file to generate the "pretty" diff, which we | 
| 1468 | 	 * append to the diffs summary file. | 
| 1469 | 	 */ | 
| 1470 |  | 
| 1471 | 	/* Write diff header */ | 
| 1472 | 	difffile = fopen(difffilename, "a" ); | 
| 1473 | 	if (difffile) | 
| 1474 | 	{ | 
| 1475 | 		fprintf(difffile, | 
| 1476 | 				"diff %s %s %s\n" , | 
| 1477 | 				pretty_diff_opts, best_expect_file, resultsfile); | 
| 1478 | 		fclose(difffile); | 
| 1479 | 	} | 
| 1480 |  | 
| 1481 | 	/* Run diff */ | 
| 1482 | 	snprintf(cmd, sizeof(cmd), | 
| 1483 | 			 "diff %s \"%s\" \"%s\" >> \"%s\"" , | 
| 1484 | 			 pretty_diff_opts, best_expect_file, resultsfile, difffilename); | 
| 1485 | 	run_diff(cmd, difffilename); | 
| 1486 |  | 
| 1487 | 	unlink(diff); | 
| 1488 | 	return true; | 
| 1489 | } | 
| 1490 |  | 
| 1491 | /* | 
| 1492 |  * Wait for specified subprocesses to finish, and return their exit | 
| 1493 |  * statuses into statuses[] and stop times into stoptimes[] | 
| 1494 |  * | 
| 1495 |  * If names isn't NULL, print each subprocess's name as it finishes | 
| 1496 |  * | 
| 1497 |  * Note: it's OK to scribble on the pids array, but not on the names array | 
| 1498 |  */ | 
| 1499 | static void | 
| 1500 | wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes, | 
| 1501 | 			   char **names, int num_tests) | 
| 1502 | { | 
| 1503 | 	int			tests_left; | 
| 1504 | 	int			i; | 
| 1505 |  | 
| 1506 | #ifdef WIN32 | 
| 1507 | 	PID_TYPE   *active_pids = pg_malloc(num_tests * sizeof(PID_TYPE)); | 
| 1508 |  | 
| 1509 | 	memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE)); | 
| 1510 | #endif | 
| 1511 |  | 
| 1512 | 	tests_left = num_tests; | 
| 1513 | 	while (tests_left > 0) | 
| 1514 | 	{ | 
| 1515 | 		PID_TYPE	p; | 
| 1516 |  | 
| 1517 | #ifndef WIN32 | 
| 1518 | 		int			exit_status; | 
| 1519 |  | 
| 1520 | 		p = wait(&exit_status); | 
| 1521 |  | 
| 1522 | 		if (p == INVALID_PID) | 
| 1523 | 		{ | 
| 1524 | 			fprintf(stderr, _("failed to wait for subprocesses: %s\n" ), | 
| 1525 | 					strerror(errno)); | 
| 1526 | 			exit(2); | 
| 1527 | 		} | 
| 1528 | #else | 
| 1529 | 		DWORD		exit_status; | 
| 1530 | 		int			r; | 
| 1531 |  | 
| 1532 | 		r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE); | 
| 1533 | 		if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left) | 
| 1534 | 		{ | 
| 1535 | 			fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n" ), | 
| 1536 | 					GetLastError()); | 
| 1537 | 			exit(2); | 
| 1538 | 		} | 
| 1539 | 		p = active_pids[r - WAIT_OBJECT_0]; | 
| 1540 | 		/* compact the active_pids array */ | 
| 1541 | 		active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1]; | 
| 1542 | #endif							/* WIN32 */ | 
| 1543 |  | 
| 1544 | 		for (i = 0; i < num_tests; i++) | 
| 1545 | 		{ | 
| 1546 | 			if (p == pids[i]) | 
| 1547 | 			{ | 
| 1548 | #ifdef WIN32 | 
| 1549 | 				GetExitCodeProcess(pids[i], &exit_status); | 
| 1550 | 				CloseHandle(pids[i]); | 
| 1551 | #endif | 
| 1552 | 				pids[i] = INVALID_PID; | 
| 1553 | 				statuses[i] = (int) exit_status; | 
| 1554 | 				INSTR_TIME_SET_CURRENT(stoptimes[i]); | 
| 1555 | 				if (names) | 
| 1556 | 					status(" %s" , names[i]); | 
| 1557 | 				tests_left--; | 
| 1558 | 				break; | 
| 1559 | 			} | 
| 1560 | 		} | 
| 1561 | 	} | 
| 1562 |  | 
| 1563 | #ifdef WIN32 | 
| 1564 | 	free(active_pids); | 
| 1565 | #endif | 
| 1566 | } | 
| 1567 |  | 
| 1568 | /* | 
| 1569 |  * report nonzero exit code from a test process | 
| 1570 |  */ | 
| 1571 | static void | 
| 1572 | log_child_failure(int exitstatus) | 
| 1573 | { | 
| 1574 | 	if (WIFEXITED(exitstatus)) | 
| 1575 | 		status(_(" (test process exited with exit code %d)" ), | 
| 1576 | 			   WEXITSTATUS(exitstatus)); | 
| 1577 | 	else if (WIFSIGNALED(exitstatus)) | 
| 1578 | 	{ | 
| 1579 | #if defined(WIN32) | 
| 1580 | 		status(_(" (test process was terminated by exception 0x%X)" ), | 
| 1581 | 			   WTERMSIG(exitstatus)); | 
| 1582 | #else | 
| 1583 | 		status(_(" (test process was terminated by signal %d: %s)" ), | 
| 1584 | 			   WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus))); | 
| 1585 | #endif | 
| 1586 | 	} | 
| 1587 | 	else | 
| 1588 | 		status(_(" (test process exited with unrecognized status %d)" ), | 
| 1589 | 			   exitstatus); | 
| 1590 | } | 
| 1591 |  | 
| 1592 | /* | 
| 1593 |  * Run all the tests specified in one schedule file | 
| 1594 |  */ | 
| 1595 | static void | 
| 1596 | run_schedule(const char *schedule, test_function tfunc) | 
| 1597 | { | 
| 1598 | #define MAX_PARALLEL_TESTS 100 | 
| 1599 | 	char	   *tests[MAX_PARALLEL_TESTS]; | 
| 1600 | 	_stringlist *resultfiles[MAX_PARALLEL_TESTS]; | 
| 1601 | 	_stringlist *expectfiles[MAX_PARALLEL_TESTS]; | 
| 1602 | 	_stringlist *tags[MAX_PARALLEL_TESTS]; | 
| 1603 | 	PID_TYPE	pids[MAX_PARALLEL_TESTS]; | 
| 1604 | 	instr_time	starttimes[MAX_PARALLEL_TESTS]; | 
| 1605 | 	instr_time	stoptimes[MAX_PARALLEL_TESTS]; | 
| 1606 | 	int			statuses[MAX_PARALLEL_TESTS]; | 
| 1607 | 	_stringlist *ignorelist = NULL; | 
| 1608 | 	char		scbuf[1024]; | 
| 1609 | 	FILE	   *scf; | 
| 1610 | 	int			line_num = 0; | 
| 1611 |  | 
| 1612 | 	memset(tests, 0, sizeof(tests)); | 
| 1613 | 	memset(resultfiles, 0, sizeof(resultfiles)); | 
| 1614 | 	memset(expectfiles, 0, sizeof(expectfiles)); | 
| 1615 | 	memset(tags, 0, sizeof(tags)); | 
| 1616 |  | 
| 1617 | 	scf = fopen(schedule, "r" ); | 
| 1618 | 	if (!scf) | 
| 1619 | 	{ | 
| 1620 | 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n" ), | 
| 1621 | 				progname, schedule, strerror(errno)); | 
| 1622 | 		exit(2); | 
| 1623 | 	} | 
| 1624 |  | 
| 1625 | 	while (fgets(scbuf, sizeof(scbuf), scf)) | 
| 1626 | 	{ | 
| 1627 | 		char	   *test = NULL; | 
| 1628 | 		char	   *c; | 
| 1629 | 		int			num_tests; | 
| 1630 | 		bool		inword; | 
| 1631 | 		int			i; | 
| 1632 |  | 
| 1633 | 		line_num++; | 
| 1634 |  | 
| 1635 | 		/* strip trailing whitespace, especially the newline */ | 
| 1636 | 		i = strlen(scbuf); | 
| 1637 | 		while (i > 0 && isspace((unsigned char) scbuf[i - 1])) | 
| 1638 | 			scbuf[--i] = '\0'; | 
| 1639 |  | 
| 1640 | 		if (scbuf[0] == '\0' || scbuf[0] == '#') | 
| 1641 | 			continue; | 
| 1642 | 		if (strncmp(scbuf, "test: " , 6) == 0) | 
| 1643 | 			test = scbuf + 6; | 
| 1644 | 		else if (strncmp(scbuf, "ignore: " , 8) == 0) | 
| 1645 | 		{ | 
| 1646 | 			c = scbuf + 8; | 
| 1647 | 			while (*c && isspace((unsigned char) *c)) | 
| 1648 | 				c++; | 
| 1649 | 			add_stringlist_item(&ignorelist, c); | 
| 1650 |  | 
| 1651 | 			/* | 
| 1652 | 			 * Note: ignore: lines do not run the test, they just say that | 
| 1653 | 			 * failure of this test when run later on is to be ignored. A bit | 
| 1654 | 			 * odd but that's how the shell-script version did it. | 
| 1655 | 			 */ | 
| 1656 | 			continue; | 
| 1657 | 		} | 
| 1658 | 		else | 
| 1659 | 		{ | 
| 1660 | 			fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n" ), | 
| 1661 | 					schedule, line_num, scbuf); | 
| 1662 | 			exit(2); | 
| 1663 | 		} | 
| 1664 |  | 
| 1665 | 		num_tests = 0; | 
| 1666 | 		inword = false; | 
| 1667 | 		for (c = test;; c++) | 
| 1668 | 		{ | 
| 1669 | 			if (*c == '\0' || isspace((unsigned char) *c)) | 
| 1670 | 			{ | 
| 1671 | 				if (inword) | 
| 1672 | 				{ | 
| 1673 | 					/* Reached end of a test name */ | 
| 1674 | 					char		sav; | 
| 1675 |  | 
| 1676 | 					if (num_tests >= MAX_PARALLEL_TESTS) | 
| 1677 | 					{ | 
| 1678 | 						fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n" ), | 
| 1679 | 								MAX_PARALLEL_TESTS, schedule, line_num, scbuf); | 
| 1680 | 						exit(2); | 
| 1681 | 					} | 
| 1682 | 					sav = *c; | 
| 1683 | 					*c = '\0'; | 
| 1684 | 					tests[num_tests] = pg_strdup(test); | 
| 1685 | 					num_tests++; | 
| 1686 | 					*c = sav; | 
| 1687 | 					inword = false; | 
| 1688 | 				} | 
| 1689 | 				if (*c == '\0') | 
| 1690 | 					break;		/* loop exit is here */ | 
| 1691 | 			} | 
| 1692 | 			else if (!inword) | 
| 1693 | 			{ | 
| 1694 | 				/* Start of a test name */ | 
| 1695 | 				test = c; | 
| 1696 | 				inword = true; | 
| 1697 | 			} | 
| 1698 | 		} | 
| 1699 |  | 
| 1700 | 		if (num_tests == 0) | 
| 1701 | 		{ | 
| 1702 | 			fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n" ), | 
| 1703 | 					schedule, line_num, scbuf); | 
| 1704 | 			exit(2); | 
| 1705 | 		} | 
| 1706 |  | 
| 1707 | 		if (num_tests == 1) | 
| 1708 | 		{ | 
| 1709 | 			status(_("test %-28s ... " ), tests[0]); | 
| 1710 | 			pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]); | 
| 1711 | 			INSTR_TIME_SET_CURRENT(starttimes[0]); | 
| 1712 | 			wait_for_tests(pids, statuses, stoptimes, NULL, 1); | 
| 1713 | 			/* status line is finished below */ | 
| 1714 | 		} | 
| 1715 | 		else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests) | 
| 1716 | 		{ | 
| 1717 | 			fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n" ), | 
| 1718 | 					max_concurrent_tests, schedule, line_num, scbuf); | 
| 1719 | 			exit(2); | 
| 1720 | 		} | 
| 1721 | 		else if (max_connections > 0 && max_connections < num_tests) | 
| 1722 | 		{ | 
| 1723 | 			int			oldest = 0; | 
| 1724 |  | 
| 1725 | 			status(_("parallel group (%d tests, in groups of %d): " ), | 
| 1726 | 				   num_tests, max_connections); | 
| 1727 | 			for (i = 0; i < num_tests; i++) | 
| 1728 | 			{ | 
| 1729 | 				if (i - oldest >= max_connections) | 
| 1730 | 				{ | 
| 1731 | 					wait_for_tests(pids + oldest, statuses + oldest, | 
| 1732 | 								   stoptimes + oldest, | 
| 1733 | 								   tests + oldest, i - oldest); | 
| 1734 | 					oldest = i; | 
| 1735 | 				} | 
| 1736 | 				pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); | 
| 1737 | 				INSTR_TIME_SET_CURRENT(starttimes[i]); | 
| 1738 | 			} | 
| 1739 | 			wait_for_tests(pids + oldest, statuses + oldest, | 
| 1740 | 						   stoptimes + oldest, | 
| 1741 | 						   tests + oldest, i - oldest); | 
| 1742 | 			status_end(); | 
| 1743 | 		} | 
| 1744 | 		else | 
| 1745 | 		{ | 
| 1746 | 			status(_("parallel group (%d tests): " ), num_tests); | 
| 1747 | 			for (i = 0; i < num_tests; i++) | 
| 1748 | 			{ | 
| 1749 | 				pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); | 
| 1750 | 				INSTR_TIME_SET_CURRENT(starttimes[i]); | 
| 1751 | 			} | 
| 1752 | 			wait_for_tests(pids, statuses, stoptimes, tests, num_tests); | 
| 1753 | 			status_end(); | 
| 1754 | 		} | 
| 1755 |  | 
| 1756 | 		/* Check results for all tests */ | 
| 1757 | 		for (i = 0; i < num_tests; i++) | 
| 1758 | 		{ | 
| 1759 | 			_stringlist *rl, | 
| 1760 | 					   *el, | 
| 1761 | 					   *tl; | 
| 1762 | 			bool		differ = false; | 
| 1763 |  | 
| 1764 | 			if (num_tests > 1) | 
| 1765 | 				status(_("     %-28s ... " ), tests[i]); | 
| 1766 |  | 
| 1767 | 			/* | 
| 1768 | 			 * Advance over all three lists simultaneously. | 
| 1769 | 			 * | 
| 1770 | 			 * Compare resultfiles[j] with expectfiles[j] always. Tags are | 
| 1771 | 			 * optional but if there are tags, the tag list has the same | 
| 1772 | 			 * length as the other two lists. | 
| 1773 | 			 */ | 
| 1774 | 			for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i]; | 
| 1775 | 				 rl != NULL;	/* rl and el have the same length */ | 
| 1776 | 				 rl = rl->next, el = el->next, | 
| 1777 | 				 tl = tl ? tl->next : NULL) | 
| 1778 | 			{ | 
| 1779 | 				bool		newdiff; | 
| 1780 |  | 
| 1781 | 				newdiff = results_differ(tests[i], rl->str, el->str); | 
| 1782 | 				if (newdiff && tl) | 
| 1783 | 				{ | 
| 1784 | 					printf("%s " , tl->str); | 
| 1785 | 				} | 
| 1786 | 				differ |= newdiff; | 
| 1787 | 			} | 
| 1788 |  | 
| 1789 | 			if (differ) | 
| 1790 | 			{ | 
| 1791 | 				bool		ignore = false; | 
| 1792 | 				_stringlist *sl; | 
| 1793 |  | 
| 1794 | 				for (sl = ignorelist; sl != NULL; sl = sl->next) | 
| 1795 | 				{ | 
| 1796 | 					if (strcmp(tests[i], sl->str) == 0) | 
| 1797 | 					{ | 
| 1798 | 						ignore = true; | 
| 1799 | 						break; | 
| 1800 | 					} | 
| 1801 | 				} | 
| 1802 | 				if (ignore) | 
| 1803 | 				{ | 
| 1804 | 					status(_("failed (ignored)" )); | 
| 1805 | 					fail_ignore_count++; | 
| 1806 | 				} | 
| 1807 | 				else | 
| 1808 | 				{ | 
| 1809 | 					status(_("FAILED" )); | 
| 1810 | 					fail_count++; | 
| 1811 | 				} | 
| 1812 | 			} | 
| 1813 | 			else | 
| 1814 | 			{ | 
| 1815 | 				status(_("ok    " ));	/* align with FAILED */ | 
| 1816 | 				success_count++; | 
| 1817 | 			} | 
| 1818 |  | 
| 1819 | 			if (statuses[i] != 0) | 
| 1820 | 				log_child_failure(statuses[i]); | 
| 1821 |  | 
| 1822 | 			INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]); | 
| 1823 | 			status(_(" %8.0f ms" ), INSTR_TIME_GET_MILLISEC(stoptimes[i])); | 
| 1824 |  | 
| 1825 | 			status_end(); | 
| 1826 | 		} | 
| 1827 |  | 
| 1828 | 		for (i = 0; i < num_tests; i++) | 
| 1829 | 		{ | 
| 1830 | 			pg_free(tests[i]); | 
| 1831 | 			tests[i] = NULL; | 
| 1832 | 			free_stringlist(&resultfiles[i]); | 
| 1833 | 			free_stringlist(&expectfiles[i]); | 
| 1834 | 			free_stringlist(&tags[i]); | 
| 1835 | 		} | 
| 1836 | 	} | 
| 1837 |  | 
| 1838 | 	free_stringlist(&ignorelist); | 
| 1839 |  | 
| 1840 | 	fclose(scf); | 
| 1841 | } | 
| 1842 |  | 
| 1843 | /* | 
| 1844 |  * Run a single test | 
| 1845 |  */ | 
| 1846 | static void | 
| 1847 | run_single_test(const char *test, test_function tfunc) | 
| 1848 | { | 
| 1849 | 	PID_TYPE	pid; | 
| 1850 | 	instr_time	starttime; | 
| 1851 | 	instr_time	stoptime; | 
| 1852 | 	int			exit_status; | 
| 1853 | 	_stringlist *resultfiles = NULL; | 
| 1854 | 	_stringlist *expectfiles = NULL; | 
| 1855 | 	_stringlist *tags = NULL; | 
| 1856 | 	_stringlist *rl, | 
| 1857 | 			   *el, | 
| 1858 | 			   *tl; | 
| 1859 | 	bool		differ = false; | 
| 1860 |  | 
| 1861 | 	status(_("test %-28s ... " ), test); | 
| 1862 | 	pid = (tfunc) (test, &resultfiles, &expectfiles, &tags); | 
| 1863 | 	INSTR_TIME_SET_CURRENT(starttime); | 
| 1864 | 	wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1); | 
| 1865 |  | 
| 1866 | 	/* | 
| 1867 | 	 * Advance over all three lists simultaneously. | 
| 1868 | 	 * | 
| 1869 | 	 * Compare resultfiles[j] with expectfiles[j] always. Tags are optional | 
| 1870 | 	 * but if there are tags, the tag list has the same length as the other | 
| 1871 | 	 * two lists. | 
| 1872 | 	 */ | 
| 1873 | 	for (rl = resultfiles, el = expectfiles, tl = tags; | 
| 1874 | 		 rl != NULL;			/* rl and el have the same length */ | 
| 1875 | 		 rl = rl->next, el = el->next, | 
| 1876 | 		 tl = tl ? tl->next : NULL) | 
| 1877 | 	{ | 
| 1878 | 		bool		newdiff; | 
| 1879 |  | 
| 1880 | 		newdiff = results_differ(test, rl->str, el->str); | 
| 1881 | 		if (newdiff && tl) | 
| 1882 | 		{ | 
| 1883 | 			printf("%s " , tl->str); | 
| 1884 | 		} | 
| 1885 | 		differ |= newdiff; | 
| 1886 | 	} | 
| 1887 |  | 
| 1888 | 	if (differ) | 
| 1889 | 	{ | 
| 1890 | 		status(_("FAILED" )); | 
| 1891 | 		fail_count++; | 
| 1892 | 	} | 
| 1893 | 	else | 
| 1894 | 	{ | 
| 1895 | 		status(_("ok    " ));	/* align with FAILED */ | 
| 1896 | 		success_count++; | 
| 1897 | 	} | 
| 1898 |  | 
| 1899 | 	if (exit_status != 0) | 
| 1900 | 		log_child_failure(exit_status); | 
| 1901 |  | 
| 1902 | 	INSTR_TIME_SUBTRACT(stoptime, starttime); | 
| 1903 | 	status(_(" %8.0f ms" ), INSTR_TIME_GET_MILLISEC(stoptime)); | 
| 1904 |  | 
| 1905 | 	status_end(); | 
| 1906 | } | 
| 1907 |  | 
| 1908 | /* | 
| 1909 |  * Create the summary-output files (making them empty if already existing) | 
| 1910 |  */ | 
| 1911 | static void | 
| 1912 | open_result_files(void) | 
| 1913 | { | 
| 1914 | 	char		file[MAXPGPATH]; | 
| 1915 | 	FILE	   *difffile; | 
| 1916 |  | 
| 1917 | 	/* create outputdir directory if not present */ | 
| 1918 | 	if (!directory_exists(outputdir)) | 
| 1919 | 		make_directory(outputdir); | 
| 1920 |  | 
| 1921 | 	/* create the log file (copy of running status output) */ | 
| 1922 | 	snprintf(file, sizeof(file), "%s/regression.out" , outputdir); | 
| 1923 | 	logfilename = pg_strdup(file); | 
| 1924 | 	logfile = fopen(logfilename, "w" ); | 
| 1925 | 	if (!logfile) | 
| 1926 | 	{ | 
| 1927 | 		fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n" ), | 
| 1928 | 				progname, logfilename, strerror(errno)); | 
| 1929 | 		exit(2); | 
| 1930 | 	} | 
| 1931 |  | 
| 1932 | 	/* create the diffs file as empty */ | 
| 1933 | 	snprintf(file, sizeof(file), "%s/regression.diffs" , outputdir); | 
| 1934 | 	difffilename = pg_strdup(file); | 
| 1935 | 	difffile = fopen(difffilename, "w" ); | 
| 1936 | 	if (!difffile) | 
| 1937 | 	{ | 
| 1938 | 		fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n" ), | 
| 1939 | 				progname, difffilename, strerror(errno)); | 
| 1940 | 		exit(2); | 
| 1941 | 	} | 
| 1942 | 	/* we don't keep the diffs file open continuously */ | 
| 1943 | 	fclose(difffile); | 
| 1944 |  | 
| 1945 | 	/* also create the results directory if not present */ | 
| 1946 | 	snprintf(file, sizeof(file), "%s/results" , outputdir); | 
| 1947 | 	if (!directory_exists(file)) | 
| 1948 | 		make_directory(file); | 
| 1949 | } | 
| 1950 |  | 
| 1951 | static void | 
| 1952 | drop_database_if_exists(const char *dbname) | 
| 1953 | { | 
| 1954 | 	header(_("dropping database \"%s\"" ), dbname); | 
| 1955 | 	psql_command("postgres" , "DROP DATABASE IF EXISTS \"%s\"" , dbname); | 
| 1956 | } | 
| 1957 |  | 
| 1958 | static void | 
| 1959 | create_database(const char *dbname) | 
| 1960 | { | 
| 1961 | 	_stringlist *sl; | 
| 1962 |  | 
| 1963 | 	/* | 
| 1964 | 	 * We use template0 so that any installation-local cruft in template1 will | 
| 1965 | 	 * not mess up the tests. | 
| 1966 | 	 */ | 
| 1967 | 	header(_("creating database \"%s\"" ), dbname); | 
| 1968 | 	if (encoding) | 
| 1969 | 		psql_command("postgres" , "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s" , dbname, encoding, | 
| 1970 | 					 (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'"  : "" ); | 
| 1971 | 	else | 
| 1972 | 		psql_command("postgres" , "CREATE DATABASE \"%s\" TEMPLATE=template0%s" , dbname, | 
| 1973 | 					 (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'"  : "" ); | 
| 1974 | 	psql_command(dbname, | 
| 1975 | 				 "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"  | 
| 1976 | 				 "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"  | 
| 1977 | 				 "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"  | 
| 1978 | 				 "ALTER DATABASE \"%s\" SET lc_time TO 'C';"  | 
| 1979 | 				 "ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"  | 
| 1980 | 				 "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';" , | 
| 1981 | 				 dbname, dbname, dbname, dbname, dbname, dbname); | 
| 1982 |  | 
| 1983 | 	/* | 
| 1984 | 	 * Install any requested procedural languages.  We use CREATE OR REPLACE | 
| 1985 | 	 * so that this will work whether or not the language is preinstalled. | 
| 1986 | 	 */ | 
| 1987 | 	for (sl = loadlanguage; sl != NULL; sl = sl->next) | 
| 1988 | 	{ | 
| 1989 | 		header(_("installing %s" ), sl->str); | 
| 1990 | 		psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"" , sl->str); | 
| 1991 | 	} | 
| 1992 |  | 
| 1993 | 	/* | 
| 1994 | 	 * Install any requested extensions.  We use CREATE IF NOT EXISTS so that | 
| 1995 | 	 * this will work whether or not the extension is preinstalled. | 
| 1996 | 	 */ | 
| 1997 | 	for (sl = loadextension; sl != NULL; sl = sl->next) | 
| 1998 | 	{ | 
| 1999 | 		header(_("installing %s" ), sl->str); | 
| 2000 | 		psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"" , sl->str); | 
| 2001 | 	} | 
| 2002 | } | 
| 2003 |  | 
| 2004 | static void | 
| 2005 | drop_role_if_exists(const char *rolename) | 
| 2006 | { | 
| 2007 | 	header(_("dropping role \"%s\"" ), rolename); | 
| 2008 | 	psql_command("postgres" , "DROP ROLE IF EXISTS \"%s\"" , rolename); | 
| 2009 | } | 
| 2010 |  | 
| 2011 | static void | 
| 2012 | create_role(const char *rolename, const _stringlist *granted_dbs) | 
| 2013 | { | 
| 2014 | 	header(_("creating role \"%s\"" ), rolename); | 
| 2015 | 	psql_command("postgres" , "CREATE ROLE \"%s\" WITH LOGIN" , rolename); | 
| 2016 | 	for (; granted_dbs != NULL; granted_dbs = granted_dbs->next) | 
| 2017 | 	{ | 
| 2018 | 		psql_command("postgres" , "GRANT ALL ON DATABASE \"%s\" TO \"%s\"" , | 
| 2019 | 					 granted_dbs->str, rolename); | 
| 2020 | 	} | 
| 2021 | } | 
| 2022 |  | 
| 2023 | static void | 
| 2024 | help(void) | 
| 2025 | { | 
| 2026 | 	printf(_("PostgreSQL regression test driver\n" )); | 
| 2027 | 	printf(_("\n" )); | 
| 2028 | 	printf(_("Usage:\n  %s [OPTION]... [EXTRA-TEST]...\n" ), progname); | 
| 2029 | 	printf(_("\n" )); | 
| 2030 | 	printf(_("Options:\n" )); | 
| 2031 | 	printf(_("      --bindir=BINPATH          use BINPATH for programs that are run;\n" )); | 
| 2032 | 	printf(_("                                if empty, use PATH from the environment\n" )); | 
| 2033 | 	printf(_("      --config-auth=DATADIR     update authentication settings for DATADIR\n" )); | 
| 2034 | 	printf(_("      --create-role=ROLE        create the specified role before testing\n" )); | 
| 2035 | 	printf(_("      --dbname=DB               use database DB (default \"regression\")\n" )); | 
| 2036 | 	printf(_("      --debug                   turn on debug mode in programs that are run\n" )); | 
| 2037 | 	printf(_("      --dlpath=DIR              look for dynamic libraries in DIR\n" )); | 
| 2038 | 	printf(_("      --encoding=ENCODING       use ENCODING as the encoding\n" )); | 
| 2039 | 	printf(_("  -h, --help                    show this help, then exit\n" )); | 
| 2040 | 	printf(_("      --inputdir=DIR            take input files from DIR (default \".\")\n" )); | 
| 2041 | 	printf(_("      --launcher=CMD            use CMD as launcher of psql\n" )); | 
| 2042 | 	printf(_("      --load-extension=EXT      load the named extension before running the\n" )); | 
| 2043 | 	printf(_("                                tests; can appear multiple times\n" )); | 
| 2044 | 	printf(_("      --load-language=LANG      load the named language before running the\n" )); | 
| 2045 | 	printf(_("                                tests; can appear multiple times\n" )); | 
| 2046 | 	printf(_("      --max-connections=N       maximum number of concurrent connections\n" )); | 
| 2047 | 	printf(_("                                (default is 0, meaning unlimited)\n" )); | 
| 2048 | 	printf(_("      --max-concurrent-tests=N  maximum number of concurrent tests in schedule\n" )); | 
| 2049 | 	printf(_("                                (default is 0, meaning unlimited)\n" )); | 
| 2050 | 	printf(_("      --outputdir=DIR           place output files in DIR (default \".\")\n" )); | 
| 2051 | 	printf(_("      --schedule=FILE           use test ordering schedule from FILE\n" )); | 
| 2052 | 	printf(_("                                (can be used multiple times to concatenate)\n" )); | 
| 2053 | 	printf(_("      --temp-instance=DIR       create a temporary instance in DIR\n" )); | 
| 2054 | 	printf(_("      --use-existing            use an existing installation\n" )); | 
| 2055 | 	printf(_("  -V, --version                 output version information, then exit\n" )); | 
| 2056 | 	printf(_("\n" )); | 
| 2057 | 	printf(_("Options for \"temp-instance\" mode:\n" )); | 
| 2058 | 	printf(_("      --no-locale               use C locale\n" )); | 
| 2059 | 	printf(_("      --port=PORT               start postmaster on PORT\n" )); | 
| 2060 | 	printf(_("      --temp-config=FILE        append contents of FILE to temporary config\n" )); | 
| 2061 | 	printf(_("\n" )); | 
| 2062 | 	printf(_("Options for using an existing installation:\n" )); | 
| 2063 | 	printf(_("      --host=HOST               use postmaster running on HOST\n" )); | 
| 2064 | 	printf(_("      --port=PORT               use postmaster running at PORT\n" )); | 
| 2065 | 	printf(_("      --user=USER               connect as USER\n" )); | 
| 2066 | 	printf(_("\n" )); | 
| 2067 | 	printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n" )); | 
| 2068 | 	printf(_("if the tests could not be run for some reason.\n" )); | 
| 2069 | 	printf(_("\n" )); | 
| 2070 | 	printf(_("Report bugs to <pgsql-bugs@lists.postgresql.org>.\n" )); | 
| 2071 | } | 
| 2072 |  | 
| 2073 | int | 
| 2074 | regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc) | 
| 2075 | { | 
| 2076 | 	static struct option long_options[] = { | 
| 2077 | 		{"help" , no_argument, NULL, 'h'}, | 
| 2078 | 		{"version" , no_argument, NULL, 'V'}, | 
| 2079 | 		{"dbname" , required_argument, NULL, 1}, | 
| 2080 | 		{"debug" , no_argument, NULL, 2}, | 
| 2081 | 		{"inputdir" , required_argument, NULL, 3}, | 
| 2082 | 		{"load-language" , required_argument, NULL, 4}, | 
| 2083 | 		{"max-connections" , required_argument, NULL, 5}, | 
| 2084 | 		{"encoding" , required_argument, NULL, 6}, | 
| 2085 | 		{"outputdir" , required_argument, NULL, 7}, | 
| 2086 | 		{"schedule" , required_argument, NULL, 8}, | 
| 2087 | 		{"temp-instance" , required_argument, NULL, 9}, | 
| 2088 | 		{"no-locale" , no_argument, NULL, 10}, | 
| 2089 | 		{"host" , required_argument, NULL, 13}, | 
| 2090 | 		{"port" , required_argument, NULL, 14}, | 
| 2091 | 		{"user" , required_argument, NULL, 15}, | 
| 2092 | 		{"bindir" , required_argument, NULL, 16}, | 
| 2093 | 		{"dlpath" , required_argument, NULL, 17}, | 
| 2094 | 		{"create-role" , required_argument, NULL, 18}, | 
| 2095 | 		{"temp-config" , required_argument, NULL, 19}, | 
| 2096 | 		{"use-existing" , no_argument, NULL, 20}, | 
| 2097 | 		{"launcher" , required_argument, NULL, 21}, | 
| 2098 | 		{"load-extension" , required_argument, NULL, 22}, | 
| 2099 | 		{"config-auth" , required_argument, NULL, 24}, | 
| 2100 | 		{"max-concurrent-tests" , required_argument, NULL, 25}, | 
| 2101 | 		{NULL, 0, NULL, 0} | 
| 2102 | 	}; | 
| 2103 |  | 
| 2104 | 	_stringlist *sl; | 
| 2105 | 	int			c; | 
| 2106 | 	int			i; | 
| 2107 | 	int			option_index; | 
| 2108 | 	char		buf[MAXPGPATH * 4]; | 
| 2109 | 	char		buf2[MAXPGPATH * 4]; | 
| 2110 |  | 
| 2111 | 	pg_logging_init(argv[0]); | 
| 2112 | 	progname = get_progname(argv[0]); | 
| 2113 | 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress" )); | 
| 2114 |  | 
| 2115 | 	get_restricted_token(); | 
| 2116 |  | 
| 2117 | 	atexit(stop_postmaster); | 
| 2118 |  | 
| 2119 | #ifndef HAVE_UNIX_SOCKETS | 
| 2120 | 	/* no unix domain sockets available, so change default */ | 
| 2121 | 	hostname = "localhost" ; | 
| 2122 | #endif | 
| 2123 |  | 
| 2124 | 	/* | 
| 2125 | 	 * We call the initialization function here because that way we can set | 
| 2126 | 	 * default parameters and let them be overwritten by the commandline. | 
| 2127 | 	 */ | 
| 2128 | 	ifunc(argc, argv); | 
| 2129 |  | 
| 2130 | 	if (getenv("PG_REGRESS_DIFF_OPTS" )) | 
| 2131 | 		pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS" ); | 
| 2132 |  | 
| 2133 | 	while ((c = getopt_long(argc, argv, "hV" , long_options, &option_index)) != -1) | 
| 2134 | 	{ | 
| 2135 | 		switch (c) | 
| 2136 | 		{ | 
| 2137 | 			case 'h': | 
| 2138 | 				help(); | 
| 2139 | 				exit(0); | 
| 2140 | 			case 'V': | 
| 2141 | 				puts("pg_regress (PostgreSQL) "  PG_VERSION); | 
| 2142 | 				exit(0); | 
| 2143 | 			case 1: | 
| 2144 |  | 
| 2145 | 				/* | 
| 2146 | 				 * If a default database was specified, we need to remove it | 
| 2147 | 				 * before we add the specified one. | 
| 2148 | 				 */ | 
| 2149 | 				free_stringlist(&dblist); | 
| 2150 | 				split_to_stringlist(optarg, "," , &dblist); | 
| 2151 | 				break; | 
| 2152 | 			case 2: | 
| 2153 | 				debug = true; | 
| 2154 | 				break; | 
| 2155 | 			case 3: | 
| 2156 | 				inputdir = pg_strdup(optarg); | 
| 2157 | 				break; | 
| 2158 | 			case 4: | 
| 2159 | 				add_stringlist_item(&loadlanguage, optarg); | 
| 2160 | 				break; | 
| 2161 | 			case 5: | 
| 2162 | 				max_connections = atoi(optarg); | 
| 2163 | 				break; | 
| 2164 | 			case 6: | 
| 2165 | 				encoding = pg_strdup(optarg); | 
| 2166 | 				break; | 
| 2167 | 			case 7: | 
| 2168 | 				outputdir = pg_strdup(optarg); | 
| 2169 | 				break; | 
| 2170 | 			case 8: | 
| 2171 | 				add_stringlist_item(&schedulelist, optarg); | 
| 2172 | 				break; | 
| 2173 | 			case 9: | 
| 2174 | 				temp_instance = make_absolute_path(optarg); | 
| 2175 | 				break; | 
| 2176 | 			case 10: | 
| 2177 | 				nolocale = true; | 
| 2178 | 				break; | 
| 2179 | 			case 13: | 
| 2180 | 				hostname = pg_strdup(optarg); | 
| 2181 | 				break; | 
| 2182 | 			case 14: | 
| 2183 | 				port = atoi(optarg); | 
| 2184 | 				port_specified_by_user = true; | 
| 2185 | 				break; | 
| 2186 | 			case 15: | 
| 2187 | 				user = pg_strdup(optarg); | 
| 2188 | 				break; | 
| 2189 | 			case 16: | 
| 2190 | 				/* "--bindir=" means to use PATH */ | 
| 2191 | 				if (strlen(optarg)) | 
| 2192 | 					bindir = pg_strdup(optarg); | 
| 2193 | 				else | 
| 2194 | 					bindir = NULL; | 
| 2195 | 				break; | 
| 2196 | 			case 17: | 
| 2197 | 				dlpath = pg_strdup(optarg); | 
| 2198 | 				break; | 
| 2199 | 			case 18: | 
| 2200 | 				split_to_stringlist(optarg, "," , &extraroles); | 
| 2201 | 				break; | 
| 2202 | 			case 19: | 
| 2203 | 				add_stringlist_item(&temp_configs, optarg); | 
| 2204 | 				break; | 
| 2205 | 			case 20: | 
| 2206 | 				use_existing = true; | 
| 2207 | 				break; | 
| 2208 | 			case 21: | 
| 2209 | 				launcher = pg_strdup(optarg); | 
| 2210 | 				break; | 
| 2211 | 			case 22: | 
| 2212 | 				add_stringlist_item(&loadextension, optarg); | 
| 2213 | 				break; | 
| 2214 | 			case 24: | 
| 2215 | 				config_auth_datadir = pg_strdup(optarg); | 
| 2216 | 				break; | 
| 2217 | 			case 25: | 
| 2218 | 				max_concurrent_tests = atoi(optarg); | 
| 2219 | 				break; | 
| 2220 | 			default: | 
| 2221 | 				/* getopt_long already emitted a complaint */ | 
| 2222 | 				fprintf(stderr, _("\nTry \"%s -h\" for more information.\n" ), | 
| 2223 | 						progname); | 
| 2224 | 				exit(2); | 
| 2225 | 		} | 
| 2226 | 	} | 
| 2227 |  | 
| 2228 | 	/* | 
| 2229 | 	 * if we still have arguments, they are extra tests to run | 
| 2230 | 	 */ | 
| 2231 | 	while (argc - optind >= 1) | 
| 2232 | 	{ | 
| 2233 | 		add_stringlist_item(&extra_tests, argv[optind]); | 
| 2234 | 		optind++; | 
| 2235 | 	} | 
| 2236 |  | 
| 2237 | 	if (config_auth_datadir) | 
| 2238 | 	{ | 
| 2239 | #ifdef ENABLE_SSPI | 
| 2240 | 		config_sspi_auth(config_auth_datadir, user); | 
| 2241 | #endif | 
| 2242 | 		exit(0); | 
| 2243 | 	} | 
| 2244 |  | 
| 2245 | 	if (temp_instance && !port_specified_by_user) | 
| 2246 |  | 
| 2247 | 		/* | 
| 2248 | 		 * To reduce chances of interference with parallel installations, use | 
| 2249 | 		 * a port number starting in the private range (49152-65535) | 
| 2250 | 		 * calculated from the version number.  This aids !HAVE_UNIX_SOCKETS | 
| 2251 | 		 * systems; elsewhere, the use of a private socket directory already | 
| 2252 | 		 * prevents interference. | 
| 2253 | 		 */ | 
| 2254 | 		port = 0xC000 | (PG_VERSION_NUM & 0x3FFF); | 
| 2255 |  | 
| 2256 | 	inputdir = make_absolute_path(inputdir); | 
| 2257 | 	outputdir = make_absolute_path(outputdir); | 
| 2258 | 	dlpath = make_absolute_path(dlpath); | 
| 2259 |  | 
| 2260 | 	/* | 
| 2261 | 	 * Initialization | 
| 2262 | 	 */ | 
| 2263 | 	open_result_files(); | 
| 2264 |  | 
| 2265 | 	initialize_environment(); | 
| 2266 |  | 
| 2267 | #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) | 
| 2268 | 	unlimit_core_size(); | 
| 2269 | #endif | 
| 2270 |  | 
| 2271 | 	if (temp_instance) | 
| 2272 | 	{ | 
| 2273 | 		FILE	   *pg_conf; | 
| 2274 | 		const char *env_wait; | 
| 2275 | 		int			wait_seconds; | 
| 2276 |  | 
| 2277 | 		/* | 
| 2278 | 		 * Prepare the temp instance | 
| 2279 | 		 */ | 
| 2280 |  | 
| 2281 | 		if (directory_exists(temp_instance)) | 
| 2282 | 		{ | 
| 2283 | 			header(_("removing existing temp instance" )); | 
| 2284 | 			if (!rmtree(temp_instance, true)) | 
| 2285 | 			{ | 
| 2286 | 				fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n" ), | 
| 2287 | 						progname, temp_instance); | 
| 2288 | 				exit(2); | 
| 2289 | 			} | 
| 2290 | 		} | 
| 2291 |  | 
| 2292 | 		header(_("creating temporary instance" )); | 
| 2293 |  | 
| 2294 | 		/* make the temp instance top directory */ | 
| 2295 | 		make_directory(temp_instance); | 
| 2296 |  | 
| 2297 | 		/* and a directory for log files */ | 
| 2298 | 		snprintf(buf, sizeof(buf), "%s/log" , outputdir); | 
| 2299 | 		if (!directory_exists(buf)) | 
| 2300 | 			make_directory(buf); | 
| 2301 |  | 
| 2302 | 		/* initdb */ | 
| 2303 | 		header(_("initializing database system" )); | 
| 2304 | 		snprintf(buf, sizeof(buf), | 
| 2305 | 				 "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1" , | 
| 2306 | 				 bindir ? bindir : "" , | 
| 2307 | 				 bindir ? "/"  : "" , | 
| 2308 | 				 temp_instance, | 
| 2309 | 				 debug ? " --debug"  : "" , | 
| 2310 | 				 nolocale ? " --no-locale"  : "" , | 
| 2311 | 				 outputdir); | 
| 2312 | 		if (system(buf)) | 
| 2313 | 		{ | 
| 2314 | 			fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n" ), progname, outputdir, buf); | 
| 2315 | 			exit(2); | 
| 2316 | 		} | 
| 2317 |  | 
| 2318 | 		/* | 
| 2319 | 		 * Adjust the default postgresql.conf for regression testing. The user | 
| 2320 | 		 * can specify a file to be appended; in any case we expand logging | 
| 2321 | 		 * and set max_prepared_transactions to enable testing of prepared | 
| 2322 | 		 * xacts.  (Note: to reduce the probability of unexpected shmmax | 
| 2323 | 		 * failures, don't set max_prepared_transactions any higher than | 
| 2324 | 		 * actually needed by the prepared_xacts regression test.) | 
| 2325 | 		 */ | 
| 2326 | 		snprintf(buf, sizeof(buf), "%s/data/postgresql.conf" , temp_instance); | 
| 2327 | 		pg_conf = fopen(buf, "a" ); | 
| 2328 | 		if (pg_conf == NULL) | 
| 2329 | 		{ | 
| 2330 | 			fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n" ), progname, buf, strerror(errno)); | 
| 2331 | 			exit(2); | 
| 2332 | 		} | 
| 2333 | 		fputs("\n# Configuration added by pg_regress\n\n" , pg_conf); | 
| 2334 | 		fputs("log_autovacuum_min_duration = 0\n" , pg_conf); | 
| 2335 | 		fputs("log_checkpoints = on\n" , pg_conf); | 
| 2336 | 		fputs("log_line_prefix = '%m [%p] %q%a '\n" , pg_conf); | 
| 2337 | 		fputs("log_lock_waits = on\n" , pg_conf); | 
| 2338 | 		fputs("log_temp_files = 128kB\n" , pg_conf); | 
| 2339 | 		fputs("max_prepared_transactions = 2\n" , pg_conf); | 
| 2340 |  | 
| 2341 | 		for (sl = temp_configs; sl != NULL; sl = sl->next) | 
| 2342 | 		{ | 
| 2343 | 			char	   *temp_config = sl->str; | 
| 2344 | 			FILE	   *; | 
| 2345 | 			char		line_buf[1024]; | 
| 2346 |  | 
| 2347 | 			extra_conf = fopen(temp_config, "r" ); | 
| 2348 | 			if (extra_conf == NULL) | 
| 2349 | 			{ | 
| 2350 | 				fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n" ), progname, temp_config, strerror(errno)); | 
| 2351 | 				exit(2); | 
| 2352 | 			} | 
| 2353 | 			while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL) | 
| 2354 | 				fputs(line_buf, pg_conf); | 
| 2355 | 			fclose(extra_conf); | 
| 2356 | 		} | 
| 2357 |  | 
| 2358 | 		fclose(pg_conf); | 
| 2359 |  | 
| 2360 | #ifdef ENABLE_SSPI | 
| 2361 |  | 
| 2362 | 		/* | 
| 2363 | 		 * Since we successfully used the same buffer for the much-longer | 
| 2364 | 		 * "initdb" command, this can't truncate. | 
| 2365 | 		 */ | 
| 2366 | 		snprintf(buf, sizeof(buf), "%s/data" , temp_instance); | 
| 2367 | 		config_sspi_auth(buf, NULL); | 
| 2368 | #elif !defined(HAVE_UNIX_SOCKETS) | 
| 2369 | #error Platform has no means to secure the test installation. | 
| 2370 | #endif | 
| 2371 |  | 
| 2372 | 		/* | 
| 2373 | 		 * Check if there is a postmaster running already. | 
| 2374 | 		 */ | 
| 2375 | 		snprintf(buf2, sizeof(buf2), | 
| 2376 | 				 "\"%s%spsql\" -X postgres <%s 2>%s" , | 
| 2377 | 				 bindir ? bindir : "" , | 
| 2378 | 				 bindir ? "/"  : "" , | 
| 2379 | 				 DEVNULL, DEVNULL); | 
| 2380 |  | 
| 2381 | 		for (i = 0; i < 16; i++) | 
| 2382 | 		{ | 
| 2383 | 			if (system(buf2) == 0) | 
| 2384 | 			{ | 
| 2385 | 				char		s[16]; | 
| 2386 |  | 
| 2387 | 				if (port_specified_by_user || i == 15) | 
| 2388 | 				{ | 
| 2389 | 					fprintf(stderr, _("port %d apparently in use\n" ), port); | 
| 2390 | 					if (!port_specified_by_user) | 
| 2391 | 						fprintf(stderr, _("%s: could not determine an available port\n" ), progname); | 
| 2392 | 					fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n" )); | 
| 2393 | 					exit(2); | 
| 2394 | 				} | 
| 2395 |  | 
| 2396 | 				fprintf(stderr, _("port %d apparently in use, trying %d\n" ), port, port + 1); | 
| 2397 | 				port++; | 
| 2398 | 				sprintf(s, "%d" , port); | 
| 2399 | 				doputenv("PGPORT" , s); | 
| 2400 | 			} | 
| 2401 | 			else | 
| 2402 | 				break; | 
| 2403 | 		} | 
| 2404 |  | 
| 2405 | 		/* | 
| 2406 | 		 * Start the temp postmaster | 
| 2407 | 		 */ | 
| 2408 | 		header(_("starting postmaster" )); | 
| 2409 | 		snprintf(buf, sizeof(buf), | 
| 2410 | 				 "\"%s%spostgres\" -D \"%s/data\" -F%s "  | 
| 2411 | 				 "-c \"listen_addresses=%s\" -k \"%s\" "  | 
| 2412 | 				 "> \"%s/log/postmaster.log\" 2>&1" , | 
| 2413 | 				 bindir ? bindir : "" , | 
| 2414 | 				 bindir ? "/"  : "" , | 
| 2415 | 				 temp_instance, debug ? " -d 5"  : "" , | 
| 2416 | 				 hostname ? hostname : "" , sockdir ? sockdir : "" , | 
| 2417 | 				 outputdir); | 
| 2418 | 		postmaster_pid = spawn_process(buf); | 
| 2419 | 		if (postmaster_pid == INVALID_PID) | 
| 2420 | 		{ | 
| 2421 | 			fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n" ), | 
| 2422 | 					progname, strerror(errno)); | 
| 2423 | 			exit(2); | 
| 2424 | 		} | 
| 2425 |  | 
| 2426 | 		/* | 
| 2427 | 		 * Wait till postmaster is able to accept connections; normally this | 
| 2428 | 		 * is only a second or so, but Cygwin is reportedly *much* slower, and | 
| 2429 | 		 * test builds using Valgrind or similar tools might be too.  Hence, | 
| 2430 | 		 * allow the default timeout of 60 seconds to be overridden from the | 
| 2431 | 		 * PGCTLTIMEOUT environment variable. | 
| 2432 | 		 */ | 
| 2433 | 		env_wait = getenv("PGCTLTIMEOUT" ); | 
| 2434 | 		if (env_wait != NULL) | 
| 2435 | 		{ | 
| 2436 | 			wait_seconds = atoi(env_wait); | 
| 2437 | 			if (wait_seconds <= 0) | 
| 2438 | 				wait_seconds = 60; | 
| 2439 | 		} | 
| 2440 | 		else | 
| 2441 | 			wait_seconds = 60; | 
| 2442 |  | 
| 2443 | 		for (i = 0; i < wait_seconds; i++) | 
| 2444 | 		{ | 
| 2445 | 			/* Done if psql succeeds */ | 
| 2446 | 			if (system(buf2) == 0) | 
| 2447 | 				break; | 
| 2448 |  | 
| 2449 | 			/* | 
| 2450 | 			 * Fail immediately if postmaster has exited | 
| 2451 | 			 */ | 
| 2452 | #ifndef WIN32 | 
| 2453 | 			if (waitpid(postmaster_pid, NULL, WNOHANG) == postmaster_pid) | 
| 2454 | #else | 
| 2455 | 			if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0) | 
| 2456 | #endif | 
| 2457 | 			{ | 
| 2458 | 				fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n" ), progname, outputdir); | 
| 2459 | 				exit(2); | 
| 2460 | 			} | 
| 2461 |  | 
| 2462 | 			pg_usleep(1000000L); | 
| 2463 | 		} | 
| 2464 | 		if (i >= wait_seconds) | 
| 2465 | 		{ | 
| 2466 | 			fprintf(stderr, _("\n%s: postmaster did not respond within %d seconds\nExamine %s/log/postmaster.log for the reason\n" ), | 
| 2467 | 					progname, wait_seconds, outputdir); | 
| 2468 |  | 
| 2469 | 			/* | 
| 2470 | 			 * If we get here, the postmaster is probably wedged somewhere in | 
| 2471 | 			 * startup.  Try to kill it ungracefully rather than leaving a | 
| 2472 | 			 * stuck postmaster that might interfere with subsequent test | 
| 2473 | 			 * attempts. | 
| 2474 | 			 */ | 
| 2475 | #ifndef WIN32 | 
| 2476 | 			if (kill(postmaster_pid, SIGKILL) != 0 && | 
| 2477 | 				errno != ESRCH) | 
| 2478 | 				fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n" ), | 
| 2479 | 						progname, strerror(errno)); | 
| 2480 | #else | 
| 2481 | 			if (TerminateProcess(postmaster_pid, 255) == 0) | 
| 2482 | 				fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n" ), | 
| 2483 | 						progname, GetLastError()); | 
| 2484 | #endif | 
| 2485 |  | 
| 2486 | 			exit(2); | 
| 2487 | 		} | 
| 2488 |  | 
| 2489 | 		postmaster_running = true; | 
| 2490 |  | 
| 2491 | #ifdef _WIN64 | 
| 2492 | /* need a series of two casts to convert HANDLE without compiler warning */ | 
| 2493 | #define ULONGPID(x) (unsigned long) (unsigned long long) (x) | 
| 2494 | #else | 
| 2495 | #define ULONGPID(x) (unsigned long) (x) | 
| 2496 | #endif | 
| 2497 | 		printf(_("running on port %d with PID %lu\n" ), | 
| 2498 | 			   port, ULONGPID(postmaster_pid)); | 
| 2499 | 	} | 
| 2500 | 	else | 
| 2501 | 	{ | 
| 2502 | 		/* | 
| 2503 | 		 * Using an existing installation, so may need to get rid of | 
| 2504 | 		 * pre-existing database(s) and role(s) | 
| 2505 | 		 */ | 
| 2506 | 		if (!use_existing) | 
| 2507 | 		{ | 
| 2508 | 			for (sl = dblist; sl; sl = sl->next) | 
| 2509 | 				drop_database_if_exists(sl->str); | 
| 2510 | 			for (sl = extraroles; sl; sl = sl->next) | 
| 2511 | 				drop_role_if_exists(sl->str); | 
| 2512 | 		} | 
| 2513 | 	} | 
| 2514 |  | 
| 2515 | 	/* | 
| 2516 | 	 * Create the test database(s) and role(s) | 
| 2517 | 	 */ | 
| 2518 | 	if (!use_existing) | 
| 2519 | 	{ | 
| 2520 | 		for (sl = dblist; sl; sl = sl->next) | 
| 2521 | 			create_database(sl->str); | 
| 2522 | 		for (sl = extraroles; sl; sl = sl->next) | 
| 2523 | 			create_role(sl->str, dblist); | 
| 2524 | 	} | 
| 2525 |  | 
| 2526 | 	/* | 
| 2527 | 	 * Ready to run the tests | 
| 2528 | 	 */ | 
| 2529 | 	header(_("running regression test queries" )); | 
| 2530 |  | 
| 2531 | 	for (sl = schedulelist; sl != NULL; sl = sl->next) | 
| 2532 | 	{ | 
| 2533 | 		run_schedule(sl->str, tfunc); | 
| 2534 | 	} | 
| 2535 |  | 
| 2536 | 	for (sl = extra_tests; sl != NULL; sl = sl->next) | 
| 2537 | 	{ | 
| 2538 | 		run_single_test(sl->str, tfunc); | 
| 2539 | 	} | 
| 2540 |  | 
| 2541 | 	/* | 
| 2542 | 	 * Shut down temp installation's postmaster | 
| 2543 | 	 */ | 
| 2544 | 	if (temp_instance) | 
| 2545 | 	{ | 
| 2546 | 		header(_("shutting down postmaster" )); | 
| 2547 | 		stop_postmaster(); | 
| 2548 | 	} | 
| 2549 |  | 
| 2550 | 	/* | 
| 2551 | 	 * If there were no errors, remove the temp instance immediately to | 
| 2552 | 	 * conserve disk space.  (If there were errors, we leave the instance in | 
| 2553 | 	 * place for possible manual investigation.) | 
| 2554 | 	 */ | 
| 2555 | 	if (temp_instance && fail_count == 0 && fail_ignore_count == 0) | 
| 2556 | 	{ | 
| 2557 | 		header(_("removing temporary instance" )); | 
| 2558 | 		if (!rmtree(temp_instance, true)) | 
| 2559 | 			fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n" ), | 
| 2560 | 					progname, temp_instance); | 
| 2561 | 	} | 
| 2562 |  | 
| 2563 | 	fclose(logfile); | 
| 2564 |  | 
| 2565 | 	/* | 
| 2566 | 	 * Emit nice-looking summary message | 
| 2567 | 	 */ | 
| 2568 | 	if (fail_count == 0 && fail_ignore_count == 0) | 
| 2569 | 		snprintf(buf, sizeof(buf), | 
| 2570 | 				 _(" All %d tests passed. " ), | 
| 2571 | 				 success_count); | 
| 2572 | 	else if (fail_count == 0)	/* fail_count=0, fail_ignore_count>0 */ | 
| 2573 | 		snprintf(buf, sizeof(buf), | 
| 2574 | 				 _(" %d of %d tests passed, %d failed test(s) ignored. " ), | 
| 2575 | 				 success_count, | 
| 2576 | 				 success_count + fail_ignore_count, | 
| 2577 | 				 fail_ignore_count); | 
| 2578 | 	else if (fail_ignore_count == 0)	/* fail_count>0 && fail_ignore_count=0 */ | 
| 2579 | 		snprintf(buf, sizeof(buf), | 
| 2580 | 				 _(" %d of %d tests failed. " ), | 
| 2581 | 				 fail_count, | 
| 2582 | 				 success_count + fail_count); | 
| 2583 | 	else | 
| 2584 | 		/* fail_count>0 && fail_ignore_count>0 */ | 
| 2585 | 		snprintf(buf, sizeof(buf), | 
| 2586 | 				 _(" %d of %d tests failed, %d of these failures ignored. " ), | 
| 2587 | 				 fail_count + fail_ignore_count, | 
| 2588 | 				 success_count + fail_count + fail_ignore_count, | 
| 2589 | 				 fail_ignore_count); | 
| 2590 |  | 
| 2591 | 	putchar('\n'); | 
| 2592 | 	for (i = strlen(buf); i > 0; i--) | 
| 2593 | 		putchar('='); | 
| 2594 | 	printf("\n%s\n" , buf); | 
| 2595 | 	for (i = strlen(buf); i > 0; i--) | 
| 2596 | 		putchar('='); | 
| 2597 | 	putchar('\n'); | 
| 2598 | 	putchar('\n'); | 
| 2599 |  | 
| 2600 | 	if (file_size(difffilename) > 0) | 
| 2601 | 	{ | 
| 2602 | 		printf(_("The differences that caused some tests to fail can be viewed in the\n"  | 
| 2603 | 				 "file \"%s\".  A copy of the test summary that you see\n"  | 
| 2604 | 				 "above is saved in the file \"%s\".\n\n" ), | 
| 2605 | 			   difffilename, logfilename); | 
| 2606 | 	} | 
| 2607 | 	else | 
| 2608 | 	{ | 
| 2609 | 		unlink(difffilename); | 
| 2610 | 		unlink(logfilename); | 
| 2611 | 	} | 
| 2612 |  | 
| 2613 | 	if (fail_count != 0) | 
| 2614 | 		exit(1); | 
| 2615 |  | 
| 2616 | 	return 0; | 
| 2617 | } | 
| 2618 |  |