PageRenderTime 51ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/test/regress/pg_regress.c

http://github.com/postgres/postgres
C | 2635 lines | 1852 code | 287 blank | 496 comment | 347 complexity | 9c1dccb23dcbc80e54470457feac1b19 MD5 | raw file
Possible License(s): AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  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-2020, 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. #include "postgres_fe.h"
  19. #include <ctype.h>
  20. #include <sys/stat.h>
  21. #include <sys/wait.h>
  22. #include <signal.h>
  23. #include <unistd.h>
  24. #ifdef HAVE_SYS_RESOURCE_H
  25. #include <sys/time.h>
  26. #include <sys/resource.h>
  27. #endif
  28. #include "common/logging.h"
  29. #include "common/restricted_token.h"
  30. #include "common/username.h"
  31. #include "getopt_long.h"
  32. #include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
  33. #include "pg_config_paths.h"
  34. #include "pg_regress.h"
  35. #include "portability/instr_time.h"
  36. /* for resultmap we need a list of pairs of strings */
  37. typedef struct _resultmap
  38. {
  39. char *test;
  40. char *type;
  41. char *resultfile;
  42. struct _resultmap *next;
  43. } _resultmap;
  44. /*
  45. * Values obtained from Makefile.
  46. */
  47. char *host_platform = HOST_TUPLE;
  48. #ifndef WIN32 /* not used in WIN32 case */
  49. static char *shellprog = SHELLPROG;
  50. #endif
  51. /*
  52. * On Windows we use -w in diff switches to avoid problems with inconsistent
  53. * newline representation. The actual result files will generally have
  54. * Windows-style newlines, but the comparison files might or might not.
  55. */
  56. #ifndef WIN32
  57. const char *basic_diff_opts = "";
  58. const char *pretty_diff_opts = "-U3";
  59. #else
  60. const char *basic_diff_opts = "-w";
  61. const char *pretty_diff_opts = "-w -U3";
  62. #endif
  63. /* options settable from command line */
  64. _stringlist *dblist = NULL;
  65. bool debug = false;
  66. char *inputdir = ".";
  67. char *outputdir = ".";
  68. char *bindir = PGBINDIR;
  69. char *launcher = NULL;
  70. static _stringlist *loadextension = NULL;
  71. static int max_connections = 0;
  72. static int max_concurrent_tests = 0;
  73. static char *encoding = NULL;
  74. static _stringlist *schedulelist = NULL;
  75. static _stringlist *extra_tests = NULL;
  76. static char *temp_instance = NULL;
  77. static _stringlist *temp_configs = NULL;
  78. static bool nolocale = false;
  79. static bool use_existing = false;
  80. static char *hostname = NULL;
  81. static int port = -1;
  82. static bool port_specified_by_user = false;
  83. static char *dlpath = PKGLIBDIR;
  84. static char *user = NULL;
  85. static _stringlist *extraroles = NULL;
  86. static char *config_auth_datadir = NULL;
  87. /* internal variables */
  88. static const char *progname;
  89. static char *logfilename;
  90. static FILE *logfile;
  91. static char *difffilename;
  92. static const char *sockdir;
  93. #ifdef HAVE_UNIX_SOCKETS
  94. static const char *temp_sockdir;
  95. static char sockself[MAXPGPATH];
  96. static char socklock[MAXPGPATH];
  97. #endif
  98. static _resultmap *resultmap = NULL;
  99. static PID_TYPE postmaster_pid = INVALID_PID;
  100. static bool postmaster_running = false;
  101. static int success_count = 0;
  102. static int fail_count = 0;
  103. static int fail_ignore_count = 0;
  104. static bool directory_exists(const char *dir);
  105. static void make_directory(const char *dir);
  106. static void header(const char *fmt,...) pg_attribute_printf(1, 2);
  107. static void status(const char *fmt,...) pg_attribute_printf(1, 2);
  108. static void psql_command(const char *database, const char *query,...) pg_attribute_printf(2, 3);
  109. /*
  110. * allow core files if possible.
  111. */
  112. #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
  113. static void
  114. unlimit_core_size(void)
  115. {
  116. struct rlimit lim;
  117. getrlimit(RLIMIT_CORE, &lim);
  118. if (lim.rlim_max == 0)
  119. {
  120. fprintf(stderr,
  121. _("%s: could not set core size: disallowed by hard limit\n"),
  122. progname);
  123. return;
  124. }
  125. else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
  126. {
  127. lim.rlim_cur = lim.rlim_max;
  128. setrlimit(RLIMIT_CORE, &lim);
  129. }
  130. }
  131. #endif
  132. /*
  133. * Add an item at the end of a stringlist.
  134. */
  135. void
  136. add_stringlist_item(_stringlist **listhead, const char *str)
  137. {
  138. _stringlist *newentry = pg_malloc(sizeof(_stringlist));
  139. _stringlist *oldentry;
  140. newentry->str = pg_strdup(str);
  141. newentry->next = NULL;
  142. if (*listhead == NULL)
  143. *listhead = newentry;
  144. else
  145. {
  146. for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
  147. /* skip */ ;
  148. oldentry->next = newentry;
  149. }
  150. }
  151. /*
  152. * Free a stringlist.
  153. */
  154. static void
  155. free_stringlist(_stringlist **listhead)
  156. {
  157. if (listhead == NULL || *listhead == NULL)
  158. return;
  159. if ((*listhead)->next != NULL)
  160. free_stringlist(&((*listhead)->next));
  161. free((*listhead)->str);
  162. free(*listhead);
  163. *listhead = NULL;
  164. }
  165. /*
  166. * Split a delimited string into a stringlist
  167. */
  168. static void
  169. split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
  170. {
  171. char *sc = pg_strdup(s);
  172. char *token = strtok(sc, delim);
  173. while (token)
  174. {
  175. add_stringlist_item(listhead, token);
  176. token = strtok(NULL, delim);
  177. }
  178. free(sc);
  179. }
  180. /*
  181. * Print a progress banner on stdout.
  182. */
  183. static void
  184. header(const char *fmt,...)
  185. {
  186. char tmp[64];
  187. va_list ap;
  188. va_start(ap, fmt);
  189. vsnprintf(tmp, sizeof(tmp), fmt, ap);
  190. va_end(ap);
  191. fprintf(stdout, "============== %-38s ==============\n", tmp);
  192. fflush(stdout);
  193. }
  194. /*
  195. * Print "doing something ..." --- supplied text should not end with newline
  196. */
  197. static void
  198. status(const char *fmt,...)
  199. {
  200. va_list ap;
  201. va_start(ap, fmt);
  202. vfprintf(stdout, fmt, ap);
  203. fflush(stdout);
  204. va_end(ap);
  205. if (logfile)
  206. {
  207. va_start(ap, fmt);
  208. vfprintf(logfile, fmt, ap);
  209. va_end(ap);
  210. }
  211. }
  212. /*
  213. * Done "doing something ..."
  214. */
  215. static void
  216. status_end(void)
  217. {
  218. fprintf(stdout, "\n");
  219. fflush(stdout);
  220. if (logfile)
  221. fprintf(logfile, "\n");
  222. }
  223. /*
  224. * shut down temp postmaster
  225. */
  226. static void
  227. stop_postmaster(void)
  228. {
  229. if (postmaster_running)
  230. {
  231. /* We use pg_ctl to issue the kill and wait for stop */
  232. char buf[MAXPGPATH * 2];
  233. int r;
  234. /* On Windows, system() seems not to force fflush, so... */
  235. fflush(stdout);
  236. fflush(stderr);
  237. snprintf(buf, sizeof(buf),
  238. "\"%s%spg_ctl\" stop -D \"%s/data\" -s",
  239. bindir ? bindir : "",
  240. bindir ? "/" : "",
  241. temp_instance);
  242. r = system(buf);
  243. if (r != 0)
  244. {
  245. fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
  246. progname, r);
  247. _exit(2); /* not exit(), that could be recursive */
  248. }
  249. postmaster_running = false;
  250. }
  251. }
  252. #ifdef HAVE_UNIX_SOCKETS
  253. /*
  254. * Remove the socket temporary directory. pg_regress never waits for a
  255. * postmaster exit, so it is indeterminate whether the postmaster has yet to
  256. * unlink the socket and lock file. Unlink them here so we can proceed to
  257. * remove the directory. Ignore errors; leaking a temporary directory is
  258. * unimportant. This can run from a signal handler. The code is not
  259. * acceptable in a Windows signal handler (see initdb.c:trapsig()), but
  260. * on Windows, pg_regress does not use Unix sockets by default.
  261. */
  262. static void
  263. remove_temp(void)
  264. {
  265. Assert(temp_sockdir);
  266. unlink(sockself);
  267. unlink(socklock);
  268. rmdir(temp_sockdir);
  269. }
  270. /*
  271. * Signal handler that calls remove_temp() and reraises the signal.
  272. */
  273. static void
  274. signal_remove_temp(int signum)
  275. {
  276. remove_temp();
  277. pqsignal(signum, SIG_DFL);
  278. raise(signum);
  279. }
  280. /*
  281. * Create a temporary directory suitable for the server's Unix-domain socket.
  282. * The directory will have mode 0700 or stricter, so no other OS user can open
  283. * our socket to exploit our use of trust authentication. Most systems
  284. * constrain the length of socket paths well below _POSIX_PATH_MAX, so we
  285. * place the directory under /tmp rather than relative to the possibly-deep
  286. * current working directory.
  287. *
  288. * Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits
  289. * testing to work in builds that relocate it to a directory not writable to
  290. * the build/test user.
  291. */
  292. static const char *
  293. make_temp_sockdir(void)
  294. {
  295. char *template = psprintf("%s/pg_regress-XXXXXX",
  296. getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp");
  297. temp_sockdir = mkdtemp(template);
  298. if (temp_sockdir == NULL)
  299. {
  300. fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
  301. progname, template, strerror(errno));
  302. exit(2);
  303. }
  304. /* Stage file names for remove_temp(). Unsafe in a signal handler. */
  305. UNIXSOCK_PATH(sockself, port, temp_sockdir);
  306. snprintf(socklock, sizeof(socklock), "%s.lock", sockself);
  307. /* Remove the directory during clean exit. */
  308. atexit(remove_temp);
  309. /*
  310. * Remove the directory before dying to the usual signals. Omit SIGQUIT,
  311. * preserving it as a quick, untidy exit.
  312. */
  313. pqsignal(SIGHUP, signal_remove_temp);
  314. pqsignal(SIGINT, signal_remove_temp);
  315. pqsignal(SIGPIPE, signal_remove_temp);
  316. pqsignal(SIGTERM, signal_remove_temp);
  317. return temp_sockdir;
  318. }
  319. #endif /* HAVE_UNIX_SOCKETS */
  320. /*
  321. * Check whether string matches pattern
  322. *
  323. * In the original shell script, this function was implemented using expr(1),
  324. * which provides basic regular expressions restricted to match starting at
  325. * the string start (in conventional regex terms, there's an implicit "^"
  326. * at the start of the pattern --- but no implicit "$" at the end).
  327. *
  328. * For now, we only support "." and ".*" as non-literal metacharacters,
  329. * because that's all that anyone has found use for in resultmap. This
  330. * code could be extended if more functionality is needed.
  331. */
  332. static bool
  333. string_matches_pattern(const char *str, const char *pattern)
  334. {
  335. while (*str && *pattern)
  336. {
  337. if (*pattern == '.' && pattern[1] == '*')
  338. {
  339. pattern += 2;
  340. /* Trailing .* matches everything. */
  341. if (*pattern == '\0')
  342. return true;
  343. /*
  344. * Otherwise, scan for a text position at which we can match the
  345. * rest of the pattern.
  346. */
  347. while (*str)
  348. {
  349. /*
  350. * Optimization to prevent most recursion: don't recurse
  351. * unless first pattern char might match this text char.
  352. */
  353. if (*str == *pattern || *pattern == '.')
  354. {
  355. if (string_matches_pattern(str, pattern))
  356. return true;
  357. }
  358. str++;
  359. }
  360. /*
  361. * End of text with no match.
  362. */
  363. return false;
  364. }
  365. else if (*pattern != '.' && *str != *pattern)
  366. {
  367. /*
  368. * Not the single-character wildcard and no explicit match? Then
  369. * time to quit...
  370. */
  371. return false;
  372. }
  373. str++;
  374. pattern++;
  375. }
  376. if (*pattern == '\0')
  377. return true; /* end of pattern, so declare match */
  378. /* End of input string. Do we have matching pattern remaining? */
  379. while (*pattern == '.' && pattern[1] == '*')
  380. pattern += 2;
  381. if (*pattern == '\0')
  382. return true; /* end of pattern, so declare match */
  383. return false;
  384. }
  385. /*
  386. * Replace all occurrences of a string in a string with a different string.
  387. * NOTE: Assumes there is enough room in the target buffer!
  388. */
  389. void
  390. replace_string(char *string, const char *replace, const char *replacement)
  391. {
  392. char *ptr;
  393. while ((ptr = strstr(string, replace)) != NULL)
  394. {
  395. char *dup = pg_strdup(string);
  396. strlcpy(string, dup, ptr - string + 1);
  397. strcat(string, replacement);
  398. strcat(string, dup + (ptr - string) + strlen(replace));
  399. free(dup);
  400. }
  401. }
  402. /*
  403. * Convert *.source found in the "source" directory, replacing certain tokens
  404. * in the file contents with their intended values, and put the resulting files
  405. * in the "dest" directory, replacing the ".source" prefix in their names with
  406. * the given suffix.
  407. */
  408. static void
  409. convert_sourcefiles_in(const char *source_subdir, const char *dest_dir, const char *dest_subdir, const char *suffix)
  410. {
  411. char testtablespace[MAXPGPATH];
  412. char indir[MAXPGPATH];
  413. struct stat st;
  414. int ret;
  415. char **name;
  416. char **names;
  417. int count = 0;
  418. snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir);
  419. /* Check that indir actually exists and is a directory */
  420. ret = stat(indir, &st);
  421. if (ret != 0 || !S_ISDIR(st.st_mode))
  422. {
  423. /*
  424. * No warning, to avoid noise in tests that do not have these
  425. * directories; for example, ecpg, contrib and src/pl.
  426. */
  427. return;
  428. }
  429. names = pgfnames(indir);
  430. if (!names)
  431. /* Error logged in pgfnames */
  432. exit(2);
  433. snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
  434. #ifdef WIN32
  435. /*
  436. * On Windows only, clean out the test tablespace dir, or create it if it
  437. * doesn't exist. On other platforms we expect the Makefile to take care
  438. * of that. (We don't migrate that functionality in here because it'd be
  439. * harder to cope with platform-specific issues such as SELinux.)
  440. *
  441. * XXX it would be better if pg_regress.c had nothing at all to do with
  442. * testtablespace, and this were handled by a .BAT file or similar on
  443. * Windows. See pgsql-hackers discussion of 2008-01-18.
  444. */
  445. if (directory_exists(testtablespace))
  446. if (!rmtree(testtablespace, true))
  447. {
  448. fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\"\n"),
  449. progname, testtablespace);
  450. exit(2);
  451. }
  452. make_directory(testtablespace);
  453. #endif
  454. /* finally loop on each file and do the replacement */
  455. for (name = names; *name; name++)
  456. {
  457. char srcfile[MAXPGPATH];
  458. char destfile[MAXPGPATH];
  459. char prefix[MAXPGPATH];
  460. FILE *infile,
  461. *outfile;
  462. char line[1024];
  463. /* reject filenames not finishing in ".source" */
  464. if (strlen(*name) < 8)
  465. continue;
  466. if (strcmp(*name + strlen(*name) - 7, ".source") != 0)
  467. continue;
  468. count++;
  469. /* build the full actual paths to open */
  470. snprintf(prefix, strlen(*name) - 6, "%s", *name);
  471. snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name);
  472. snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir,
  473. prefix, suffix);
  474. infile = fopen(srcfile, "r");
  475. if (!infile)
  476. {
  477. fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
  478. progname, srcfile, strerror(errno));
  479. exit(2);
  480. }
  481. outfile = fopen(destfile, "w");
  482. if (!outfile)
  483. {
  484. fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  485. progname, destfile, strerror(errno));
  486. exit(2);
  487. }
  488. while (fgets(line, sizeof(line), infile))
  489. {
  490. replace_string(line, "@abs_srcdir@", inputdir);
  491. replace_string(line, "@abs_builddir@", outputdir);
  492. replace_string(line, "@testtablespace@", testtablespace);
  493. replace_string(line, "@libdir@", dlpath);
  494. replace_string(line, "@DLSUFFIX@", DLSUFFIX);
  495. fputs(line, outfile);
  496. }
  497. fclose(infile);
  498. fclose(outfile);
  499. }
  500. /*
  501. * If we didn't process any files, complain because it probably means
  502. * somebody neglected to pass the needed --inputdir argument.
  503. */
  504. if (count <= 0)
  505. {
  506. fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"),
  507. progname, indir);
  508. exit(2);
  509. }
  510. pgfnames_cleanup(names);
  511. }
  512. /* Create the .sql and .out files from the .source files, if any */
  513. static void
  514. convert_sourcefiles(void)
  515. {
  516. convert_sourcefiles_in("input", outputdir, "sql", "sql");
  517. convert_sourcefiles_in("output", outputdir, "expected", "out");
  518. }
  519. /*
  520. * Scan resultmap file to find which platform-specific expected files to use.
  521. *
  522. * The format of each line of the file is
  523. * testname/hostplatformpattern=substitutefile
  524. * where the hostplatformpattern is evaluated per the rules of expr(1),
  525. * namely, it is a standard regular expression with an implicit ^ at the start.
  526. * (We currently support only a very limited subset of regular expressions,
  527. * see string_matches_pattern() above.) What hostplatformpattern will be
  528. * matched against is the config.guess output. (In the shell-script version,
  529. * we also provided an indication of whether gcc or another compiler was in
  530. * use, but that facility isn't used anymore.)
  531. */
  532. static void
  533. load_resultmap(void)
  534. {
  535. char buf[MAXPGPATH];
  536. FILE *f;
  537. /* scan the file ... */
  538. snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
  539. f = fopen(buf, "r");
  540. if (!f)
  541. {
  542. /* OK if it doesn't exist, else complain */
  543. if (errno == ENOENT)
  544. return;
  545. fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
  546. progname, buf, strerror(errno));
  547. exit(2);
  548. }
  549. while (fgets(buf, sizeof(buf), f))
  550. {
  551. char *platform;
  552. char *file_type;
  553. char *expected;
  554. int i;
  555. /* strip trailing whitespace, especially the newline */
  556. i = strlen(buf);
  557. while (i > 0 && isspace((unsigned char) buf[i - 1]))
  558. buf[--i] = '\0';
  559. /* parse out the line fields */
  560. file_type = strchr(buf, ':');
  561. if (!file_type)
  562. {
  563. fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
  564. buf);
  565. exit(2);
  566. }
  567. *file_type++ = '\0';
  568. platform = strchr(file_type, ':');
  569. if (!platform)
  570. {
  571. fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
  572. buf);
  573. exit(2);
  574. }
  575. *platform++ = '\0';
  576. expected = strchr(platform, '=');
  577. if (!expected)
  578. {
  579. fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
  580. buf);
  581. exit(2);
  582. }
  583. *expected++ = '\0';
  584. /*
  585. * if it's for current platform, save it in resultmap list. Note: by
  586. * adding at the front of the list, we ensure that in ambiguous cases,
  587. * the last match in the resultmap file is used. This mimics the
  588. * behavior of the old shell script.
  589. */
  590. if (string_matches_pattern(host_platform, platform))
  591. {
  592. _resultmap *entry = pg_malloc(sizeof(_resultmap));
  593. entry->test = pg_strdup(buf);
  594. entry->type = pg_strdup(file_type);
  595. entry->resultfile = pg_strdup(expected);
  596. entry->next = resultmap;
  597. resultmap = entry;
  598. }
  599. }
  600. fclose(f);
  601. }
  602. /*
  603. * Check in resultmap if we should be looking at a different file
  604. */
  605. static
  606. const char *
  607. get_expectfile(const char *testname, const char *file)
  608. {
  609. char *file_type;
  610. _resultmap *rm;
  611. /*
  612. * Determine the file type from the file name. This is just what is
  613. * following the last dot in the file name.
  614. */
  615. if (!file || !(file_type = strrchr(file, '.')))
  616. return NULL;
  617. file_type++;
  618. for (rm = resultmap; rm != NULL; rm = rm->next)
  619. {
  620. if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
  621. {
  622. return rm->resultfile;
  623. }
  624. }
  625. return NULL;
  626. }
  627. /*
  628. * Handy subroutine for setting an environment variable "var" to "val"
  629. */
  630. static void
  631. doputenv(const char *var, const char *val)
  632. {
  633. char *s;
  634. s = psprintf("%s=%s", var, val);
  635. putenv(s);
  636. }
  637. /*
  638. * Prepare environment variables for running regression tests
  639. */
  640. static void
  641. initialize_environment(void)
  642. {
  643. /*
  644. * Set default application_name. (The test_function may choose to
  645. * override this, but if it doesn't, we have something useful in place.)
  646. */
  647. putenv("PGAPPNAME=pg_regress");
  648. if (nolocale)
  649. {
  650. /*
  651. * Clear out any non-C locale settings
  652. */
  653. unsetenv("LC_COLLATE");
  654. unsetenv("LC_CTYPE");
  655. unsetenv("LC_MONETARY");
  656. unsetenv("LC_NUMERIC");
  657. unsetenv("LC_TIME");
  658. unsetenv("LANG");
  659. /*
  660. * Most platforms have adopted the POSIX locale as their
  661. * implementation-defined default locale. Exceptions include native
  662. * Windows, macOS with --enable-nls, and Cygwin with --enable-nls.
  663. * (Use of --enable-nls matters because libintl replaces setlocale().)
  664. * Also, PostgreSQL does not support macOS with locale environment
  665. * variables unset; see PostmasterMain().
  666. */
  667. #if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
  668. putenv("LANG=C");
  669. #endif
  670. }
  671. /*
  672. * Set translation-related settings to English; otherwise psql will
  673. * produce translated messages and produce diffs. (XXX If we ever support
  674. * translation of pg_regress, this needs to be moved elsewhere, where psql
  675. * is actually called.)
  676. */
  677. unsetenv("LANGUAGE");
  678. unsetenv("LC_ALL");
  679. putenv("LC_MESSAGES=C");
  680. /*
  681. * Set encoding as requested
  682. */
  683. if (encoding)
  684. doputenv("PGCLIENTENCODING", encoding);
  685. else
  686. unsetenv("PGCLIENTENCODING");
  687. /*
  688. * Set timezone and datestyle for datetime-related tests
  689. */
  690. putenv("PGTZ=PST8PDT");
  691. putenv("PGDATESTYLE=Postgres, MDY");
  692. /*
  693. * Likewise set intervalstyle to ensure consistent results. This is a bit
  694. * more painful because we must use PGOPTIONS, and we want to preserve the
  695. * user's ability to set other variables through that.
  696. */
  697. {
  698. const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
  699. const char *old_pgoptions = getenv("PGOPTIONS");
  700. char *new_pgoptions;
  701. if (!old_pgoptions)
  702. old_pgoptions = "";
  703. new_pgoptions = psprintf("PGOPTIONS=%s %s",
  704. old_pgoptions, my_pgoptions);
  705. putenv(new_pgoptions);
  706. }
  707. if (temp_instance)
  708. {
  709. /*
  710. * Clear out any environment vars that might cause psql to connect to
  711. * the wrong postmaster, or otherwise behave in nondefault ways. (Note
  712. * we also use psql's -X switch consistently, so that ~/.psqlrc files
  713. * won't mess things up.) Also, set PGPORT to the temp port, and set
  714. * PGHOST depending on whether we are using TCP or Unix sockets.
  715. */
  716. unsetenv("PGDATABASE");
  717. unsetenv("PGUSER");
  718. unsetenv("PGSERVICE");
  719. unsetenv("PGSSLMODE");
  720. unsetenv("PGREQUIRESSL");
  721. unsetenv("PGCONNECT_TIMEOUT");
  722. unsetenv("PGDATA");
  723. #ifdef HAVE_UNIX_SOCKETS
  724. if (hostname != NULL)
  725. doputenv("PGHOST", hostname);
  726. else
  727. {
  728. sockdir = getenv("PG_REGRESS_SOCK_DIR");
  729. if (!sockdir)
  730. sockdir = make_temp_sockdir();
  731. doputenv("PGHOST", sockdir);
  732. }
  733. #else
  734. Assert(hostname != NULL);
  735. doputenv("PGHOST", hostname);
  736. #endif
  737. unsetenv("PGHOSTADDR");
  738. if (port != -1)
  739. {
  740. char s[16];
  741. sprintf(s, "%d", port);
  742. doputenv("PGPORT", s);
  743. }
  744. }
  745. else
  746. {
  747. const char *pghost;
  748. const char *pgport;
  749. /*
  750. * When testing an existing install, we honor existing environment
  751. * variables, except if they're overridden by command line options.
  752. */
  753. if (hostname != NULL)
  754. {
  755. doputenv("PGHOST", hostname);
  756. unsetenv("PGHOSTADDR");
  757. }
  758. if (port != -1)
  759. {
  760. char s[16];
  761. sprintf(s, "%d", port);
  762. doputenv("PGPORT", s);
  763. }
  764. if (user != NULL)
  765. doputenv("PGUSER", user);
  766. /*
  767. * However, we *don't* honor PGDATABASE, since we certainly don't wish
  768. * to connect to whatever database the user might like as default.
  769. * (Most tests override PGDATABASE anyway, but there are some ECPG
  770. * test cases that don't.)
  771. */
  772. unsetenv("PGDATABASE");
  773. /*
  774. * Report what we're connecting to
  775. */
  776. pghost = getenv("PGHOST");
  777. pgport = getenv("PGPORT");
  778. #ifndef HAVE_UNIX_SOCKETS
  779. if (!pghost)
  780. pghost = "localhost";
  781. #endif
  782. if (pghost && pgport)
  783. printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport);
  784. if (pghost && !pgport)
  785. printf(_("(using postmaster on %s, default port)\n"), pghost);
  786. if (!pghost && pgport)
  787. printf(_("(using postmaster on Unix socket, port %s)\n"), pgport);
  788. if (!pghost && !pgport)
  789. printf(_("(using postmaster on Unix socket, default port)\n"));
  790. }
  791. convert_sourcefiles();
  792. load_resultmap();
  793. }
  794. #ifdef ENABLE_SSPI
  795. /* support for config_sspi_auth() */
  796. static const char *
  797. fmtHba(const char *raw)
  798. {
  799. static char *ret;
  800. const char *rp;
  801. char *wp;
  802. wp = ret = realloc(ret, 3 + strlen(raw) * 2);
  803. *wp++ = '"';
  804. for (rp = raw; *rp; rp++)
  805. {
  806. if (*rp == '"')
  807. *wp++ = '"';
  808. *wp++ = *rp;
  809. }
  810. *wp++ = '"';
  811. *wp++ = '\0';
  812. return ret;
  813. }
  814. /*
  815. * Get account and domain/realm names for the current user. This is based on
  816. * pg_SSPI_recvauth(). The returned strings use static storage.
  817. */
  818. static void
  819. current_windows_user(const char **acct, const char **dom)
  820. {
  821. static char accountname[MAXPGPATH];
  822. static char domainname[MAXPGPATH];
  823. HANDLE token;
  824. TOKEN_USER *tokenuser;
  825. DWORD retlen;
  826. DWORD accountnamesize = sizeof(accountname);
  827. DWORD domainnamesize = sizeof(domainname);
  828. SID_NAME_USE accountnameuse;
  829. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
  830. {
  831. fprintf(stderr,
  832. _("%s: could not open process token: error code %lu\n"),
  833. progname, GetLastError());
  834. exit(2);
  835. }
  836. if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
  837. {
  838. fprintf(stderr,
  839. _("%s: could not get token information buffer size: error code %lu\n"),
  840. progname, GetLastError());
  841. exit(2);
  842. }
  843. tokenuser = pg_malloc(retlen);
  844. if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
  845. {
  846. fprintf(stderr,
  847. _("%s: could not get token information: error code %lu\n"),
  848. progname, GetLastError());
  849. exit(2);
  850. }
  851. if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
  852. domainname, &domainnamesize, &accountnameuse))
  853. {
  854. fprintf(stderr,
  855. _("%s: could not look up account SID: error code %lu\n"),
  856. progname, GetLastError());
  857. exit(2);
  858. }
  859. free(tokenuser);
  860. *acct = accountname;
  861. *dom = domainname;
  862. }
  863. /*
  864. * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
  865. * the current OS user to authenticate as the bootstrap superuser and as any
  866. * user named in a --create-role option.
  867. *
  868. * In --config-auth mode, the --user switch can be used to specify the
  869. * bootstrap superuser's name, otherwise we assume it is the default.
  870. */
  871. static void
  872. config_sspi_auth(const char *pgdata, const char *superuser_name)
  873. {
  874. const char *accountname,
  875. *domainname;
  876. char *errstr;
  877. bool have_ipv6;
  878. char fname[MAXPGPATH];
  879. int res;
  880. FILE *hba,
  881. *ident;
  882. _stringlist *sl;
  883. /* Find out the name of the current OS user */
  884. current_windows_user(&accountname, &domainname);
  885. /* Determine the bootstrap superuser's name */
  886. if (superuser_name == NULL)
  887. {
  888. /*
  889. * Compute the default superuser name the same way initdb does.
  890. *
  891. * It's possible that this result always matches "accountname", the
  892. * value SSPI authentication discovers. But the underlying system
  893. * functions do not clearly guarantee that.
  894. */
  895. superuser_name = get_user_name(&errstr);
  896. if (superuser_name == NULL)
  897. {
  898. fprintf(stderr, "%s: %s\n", progname, errstr);
  899. exit(2);
  900. }
  901. }
  902. /*
  903. * Like initdb.c:setup_config(), determine whether the platform recognizes
  904. * ::1 (IPv6 loopback) as a numeric host address string.
  905. */
  906. {
  907. struct addrinfo *gai_result;
  908. struct addrinfo hints;
  909. WSADATA wsaData;
  910. hints.ai_flags = AI_NUMERICHOST;
  911. hints.ai_family = AF_UNSPEC;
  912. hints.ai_socktype = 0;
  913. hints.ai_protocol = 0;
  914. hints.ai_addrlen = 0;
  915. hints.ai_canonname = NULL;
  916. hints.ai_addr = NULL;
  917. hints.ai_next = NULL;
  918. have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 &&
  919. getaddrinfo("::1", NULL, &hints, &gai_result) == 0);
  920. }
  921. /* Check a Write outcome and report any error. */
  922. #define CW(cond) \
  923. do { \
  924. if (!(cond)) \
  925. { \
  926. fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \
  927. progname, fname, strerror(errno)); \
  928. exit(2); \
  929. } \
  930. } while (0)
  931. res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
  932. if (res < 0 || res >= sizeof(fname))
  933. {
  934. /*
  935. * Truncating this name is a fatal error, because we must not fail to
  936. * overwrite an original trust-authentication pg_hba.conf.
  937. */
  938. fprintf(stderr, _("%s: directory name too long\n"), progname);
  939. exit(2);
  940. }
  941. hba = fopen(fname, "w");
  942. if (hba == NULL)
  943. {
  944. fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  945. progname, fname, strerror(errno));
  946. exit(2);
  947. }
  948. CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
  949. CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
  950. hba) >= 0);
  951. if (have_ipv6)
  952. CW(fputs("host all all ::1/128 sspi include_realm=1 map=regress\n",
  953. hba) >= 0);
  954. CW(fclose(hba) == 0);
  955. snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
  956. ident = fopen(fname, "w");
  957. if (ident == NULL)
  958. {
  959. fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  960. progname, fname, strerror(errno));
  961. exit(2);
  962. }
  963. CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
  964. /*
  965. * Double-quote for the benefit of account names containing whitespace or
  966. * '#'. Windows forbids the double-quote character itself, so don't
  967. * bother escaping embedded double-quote characters.
  968. */
  969. CW(fprintf(ident, "regress \"%s@%s\" %s\n",
  970. accountname, domainname, fmtHba(superuser_name)) >= 0);
  971. for (sl = extraroles; sl; sl = sl->next)
  972. CW(fprintf(ident, "regress \"%s@%s\" %s\n",
  973. accountname, domainname, fmtHba(sl->str)) >= 0);
  974. CW(fclose(ident) == 0);
  975. }
  976. #endif /* ENABLE_SSPI */
  977. /*
  978. * Issue a command via psql, connecting to the specified database
  979. *
  980. * Since we use system(), this doesn't return until the operation finishes
  981. */
  982. static void
  983. psql_command(const char *database, const char *query,...)
  984. {
  985. char query_formatted[1024];
  986. char query_escaped[2048];
  987. char psql_cmd[MAXPGPATH + 2048];
  988. va_list args;
  989. char *s;
  990. char *d;
  991. /* Generate the query with insertion of sprintf arguments */
  992. va_start(args, query);
  993. vsnprintf(query_formatted, sizeof(query_formatted), query, args);
  994. va_end(args);
  995. /* Now escape any shell double-quote metacharacters */
  996. d = query_escaped;
  997. for (s = query_formatted; *s; s++)
  998. {
  999. if (strchr("\\\"$`", *s))
  1000. *d++ = '\\';
  1001. *d++ = *s;
  1002. }
  1003. *d = '\0';
  1004. /* And now we can build and execute the shell command */
  1005. snprintf(psql_cmd, sizeof(psql_cmd),
  1006. "\"%s%spsql\" -X -c \"%s\" \"%s\"",
  1007. bindir ? bindir : "",
  1008. bindir ? "/" : "",
  1009. query_escaped,
  1010. database);
  1011. if (system(psql_cmd) != 0)
  1012. {
  1013. /* psql probably already reported the error */
  1014. fprintf(stderr, _("command failed: %s\n"), psql_cmd);
  1015. exit(2);
  1016. }
  1017. }
  1018. /*
  1019. * Spawn a process to execute the given shell command; don't wait for it
  1020. *
  1021. * Returns the process ID (or HANDLE) so we can wait for it later
  1022. */
  1023. PID_TYPE
  1024. spawn_process(const char *cmdline)
  1025. {
  1026. #ifndef WIN32
  1027. pid_t pid;
  1028. /*
  1029. * Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here
  1030. * ... does anyone still care about systems where that doesn't work?
  1031. */
  1032. fflush(stdout);
  1033. fflush(stderr);
  1034. if (logfile)
  1035. fflush(logfile);
  1036. pid = fork();
  1037. if (pid == -1)
  1038. {
  1039. fprintf(stderr, _("%s: could not fork: %s\n"),
  1040. progname, strerror(errno));
  1041. exit(2);
  1042. }
  1043. if (pid == 0)
  1044. {
  1045. /*
  1046. * In child
  1047. *
  1048. * Instead of using system(), exec the shell directly, and tell it to
  1049. * "exec" the command too. This saves two useless processes per
  1050. * parallel test case.
  1051. */
  1052. char *cmdline2;
  1053. cmdline2 = psprintf("exec %s", cmdline);
  1054. execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
  1055. fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
  1056. progname, shellprog, strerror(errno));
  1057. _exit(1); /* not exit() here... */
  1058. }
  1059. /* in parent */
  1060. return pid;
  1061. #else
  1062. PROCESS_INFORMATION pi;
  1063. char *cmdline2;
  1064. HANDLE restrictedToken;
  1065. const char *comspec;
  1066. /* Find CMD.EXE location using COMSPEC, if it's set */
  1067. comspec = getenv("COMSPEC");
  1068. if (comspec == NULL)
  1069. comspec = "CMD";
  1070. memset(&pi, 0, sizeof(pi));
  1071. cmdline2 = psprintf("\"%s\" /c \"%s\"", comspec, cmdline);
  1072. if ((restrictedToken =
  1073. CreateRestrictedProcess(cmdline2, &pi)) == 0)
  1074. exit(2);
  1075. CloseHandle(pi.hThread);
  1076. return pi.hProcess;
  1077. #endif
  1078. }
  1079. /*
  1080. * Count bytes in file
  1081. */
  1082. static long
  1083. file_size(const char *file)
  1084. {
  1085. long r;
  1086. FILE *f = fopen(file, "r");
  1087. if (!f)
  1088. {
  1089. fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
  1090. progname, file, strerror(errno));
  1091. return -1;
  1092. }
  1093. fseek(f, 0, SEEK_END);
  1094. r = ftell(f);
  1095. fclose(f);
  1096. return r;
  1097. }
  1098. /*
  1099. * Count lines in file
  1100. */
  1101. static int
  1102. file_line_count(const char *file)
  1103. {
  1104. int c;
  1105. int l = 0;
  1106. FILE *f = fopen(file, "r");
  1107. if (!f)
  1108. {
  1109. fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
  1110. progname, file, strerror(errno));
  1111. return -1;
  1112. }
  1113. while ((c = fgetc(f)) != EOF)
  1114. {
  1115. if (c == '\n')
  1116. l++;
  1117. }
  1118. fclose(f);
  1119. return l;
  1120. }
  1121. bool
  1122. file_exists(const char *file)
  1123. {
  1124. FILE *f = fopen(file, "r");
  1125. if (!f)
  1126. return false;
  1127. fclose(f);
  1128. return true;
  1129. }
  1130. static bool
  1131. directory_exists(const char *dir)
  1132. {
  1133. struct stat st;
  1134. if (stat(dir, &st) != 0)
  1135. return false;
  1136. if (S_ISDIR(st.st_mode))
  1137. return true;
  1138. return false;
  1139. }
  1140. /* Create a directory */
  1141. static void
  1142. make_directory(const char *dir)
  1143. {
  1144. if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
  1145. {
  1146. fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
  1147. progname, dir, strerror(errno));
  1148. exit(2);
  1149. }
  1150. }
  1151. /*
  1152. * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
  1153. */
  1154. static char *
  1155. get_alternative_expectfile(const char *expectfile, int i)
  1156. {
  1157. char *last_dot;
  1158. int ssize = strlen(expectfile) + 2 + 1;
  1159. char *tmp;
  1160. char *s;
  1161. if (!(tmp = (char *) malloc(ssize)))
  1162. return NULL;
  1163. if (!(s = (char *) malloc(ssize)))
  1164. {
  1165. free(tmp);
  1166. return NULL;
  1167. }
  1168. strcpy(tmp, expectfile);
  1169. last_dot = strrchr(tmp, '.');
  1170. if (!last_dot)
  1171. {
  1172. free(tmp);
  1173. free(s);
  1174. return NULL;
  1175. }
  1176. *last_dot = '\0';
  1177. snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
  1178. free(tmp);
  1179. return s;
  1180. }
  1181. /*
  1182. * Run a "diff" command and also check that it didn't crash
  1183. */
  1184. static int
  1185. run_diff(const char *cmd, const char *filename)
  1186. {
  1187. int r;
  1188. r = system(cmd);
  1189. if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
  1190. {
  1191. fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
  1192. exit(2);
  1193. }
  1194. #ifdef WIN32
  1195. /*
  1196. * On WIN32, if the 'diff' command cannot be found, system() returns 1,
  1197. * but produces nothing to stdout, so we check for that here.
  1198. */
  1199. if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
  1200. {
  1201. fprintf(stderr, _("diff command not found: %s\n"), cmd);
  1202. exit(2);
  1203. }
  1204. #endif
  1205. return WEXITSTATUS(r);
  1206. }
  1207. /*
  1208. * Check the actual result file for the given test against expected results
  1209. *
  1210. * Returns true if different (failure), false if correct match found.
  1211. * In the true case, the diff is appended to the diffs file.
  1212. */
  1213. static bool
  1214. results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
  1215. {
  1216. char expectfile[MAXPGPATH];
  1217. char diff[MAXPGPATH];
  1218. char cmd[MAXPGPATH * 3];
  1219. char best_expect_file[MAXPGPATH];
  1220. FILE *difffile;
  1221. int best_line_count;
  1222. int i;
  1223. int l;
  1224. const char *platform_expectfile;
  1225. /*
  1226. * We can pass either the resultsfile or the expectfile, they should have
  1227. * the same type (filename.type) anyway.
  1228. */
  1229. platform_expectfile = get_expectfile(testname, resultsfile);
  1230. strlcpy(expectfile, default_expectfile, sizeof(expectfile));
  1231. if (platform_expectfile)
  1232. {
  1233. /*
  1234. * Replace everything after the last slash in expectfile with what the
  1235. * platform_expectfile contains.
  1236. */
  1237. char *p = strrchr(expectfile, '/');
  1238. if (p)
  1239. strcpy(++p, platform_expectfile);
  1240. }
  1241. /* Name to use for temporary diff file */
  1242. snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
  1243. /* OK, run the diff */
  1244. snprintf(cmd, sizeof(cmd),
  1245. "diff %s \"%s\" \"%s\" > \"%s\"",
  1246. basic_diff_opts, expectfile, resultsfile, diff);
  1247. /* Is the diff file empty? */
  1248. if (run_diff(cmd, diff) == 0)
  1249. {
  1250. unlink(diff);
  1251. return false;
  1252. }
  1253. /* There may be secondary comparison files that match better */
  1254. best_line_count = file_line_count(diff);
  1255. strcpy(best_expect_file, expectfile);
  1256. for (i = 0; i <= 9; i++)
  1257. {
  1258. char *alt_expectfile;
  1259. alt_expectfile = get_alternative_expectfile(expectfile, i);
  1260. if (!alt_expectfile)
  1261. {
  1262. fprintf(stderr, _("Unable to check secondary comparison files: %s\n"),
  1263. strerror(errno));
  1264. exit(2);
  1265. }
  1266. if (!file_exists(alt_expectfile))
  1267. {
  1268. free(alt_expectfile);
  1269. continue;
  1270. }
  1271. snprintf(cmd, sizeof(cmd),
  1272. "diff %s \"%s\" \"%s\" > \"%s\"",
  1273. basic_diff_opts, alt_expectfile, resultsfile, diff);
  1274. if (run_diff(cmd, diff) == 0)
  1275. {
  1276. unlink(diff);
  1277. free(alt_expectfile);
  1278. return false;
  1279. }
  1280. l = file_line_count(diff);
  1281. if (l < best_line_count)
  1282. {
  1283. /* This diff was a better match than the last one */
  1284. best_line_count = l;
  1285. strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file));
  1286. }
  1287. free(alt_expectfile);
  1288. }
  1289. /*
  1290. * fall back on the canonical results file if we haven't tried it yet and
  1291. * haven't found a complete match yet.
  1292. */
  1293. if (platform_expectfile)
  1294. {
  1295. snprintf(cmd, sizeof(cmd),
  1296. "diff %s \"%s\" \"%s\" > \"%s\"",
  1297. basic_diff_opts, default_expectfile, resultsfile, diff);
  1298. if (run_diff(cmd, diff) == 0)
  1299. {
  1300. /* No diff = no changes = good */
  1301. unlink(diff);
  1302. return false;
  1303. }
  1304. l = file_line_count(diff);
  1305. if (l < best_line_count)
  1306. {
  1307. /* This diff was a better match than the last one */
  1308. best_line_count = l;
  1309. strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file));
  1310. }
  1311. }
  1312. /*
  1313. * Use the best comparison file to generate the "pretty" diff, which we
  1314. * append to the diffs summary file.
  1315. */
  1316. /* Write diff header */
  1317. difffile = fopen(difffilename, "a");
  1318. if (difffile)
  1319. {
  1320. fprintf(difffile,
  1321. "diff %s %s %s\n",
  1322. pretty_diff_opts, best_expect_file, resultsfile);
  1323. fclose(difffile);
  1324. }
  1325. /* Run diff */
  1326. snprintf(cmd, sizeof(cmd),
  1327. "diff %s \"%s\" \"%s\" >> \"%s\"",
  1328. pretty_diff_opts, best_expect_file, resultsfile, difffilename);
  1329. run_diff(cmd, difffilename);
  1330. unlink(diff);
  1331. return true;
  1332. }
  1333. /*
  1334. * Wait for specified subprocesses to finish, and return their exit
  1335. * statuses into statuses[] and stop times into stoptimes[]
  1336. *
  1337. * If names isn't NULL, print each subprocess's name as it finishes
  1338. *
  1339. * Note: it's OK to scribble on the pids array, but not on the names array
  1340. */
  1341. static void
  1342. wait_for_tests(PID_TYPE * pids, int *statuses, instr_time *stoptimes,
  1343. char **names, int num_tests)
  1344. {
  1345. int tests_left;
  1346. int i;
  1347. #ifdef WIN32
  1348. PID_TYPE *active_pids = pg_malloc(num_tests * sizeof(PID_TYPE));
  1349. memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
  1350. #endif
  1351. tests_left = num_tests;
  1352. while (tests_left > 0)
  1353. {
  1354. PID_TYPE p;
  1355. #ifndef WIN32
  1356. int exit_status;
  1357. p = wait(&exit_status);
  1358. if (p == INVALID_PID)
  1359. {
  1360. fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
  1361. strerror(errno));
  1362. exit(2);
  1363. }
  1364. #else
  1365. DWORD exit_status;
  1366. int r;
  1367. r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
  1368. if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
  1369. {
  1370. fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"),
  1371. GetLastError());
  1372. exit(2);
  1373. }
  1374. p = active_pids[r - WAIT_OBJECT_0];
  1375. /* compact the active_pids array */
  1376. active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
  1377. #endif /* WIN32 */
  1378. for (i = 0; i < num_tests; i++)
  1379. {
  1380. if (p == pids[i])
  1381. {
  1382. #ifdef WIN32
  1383. GetExitCodeProcess(pids[i], &exit_status);
  1384. CloseHandle(pids[i]);
  1385. #endif
  1386. pids[i] = INVALID_PID;
  1387. statuses[i] = (int) exit_status;
  1388. INSTR_TIME_SET_CURRENT(stoptimes[i]);
  1389. if (names)
  1390. status(" %s", names[i]);
  1391. tests_left--;
  1392. break;
  1393. }
  1394. }
  1395. }
  1396. #ifdef WIN32
  1397. free(active_pids);
  1398. #endif
  1399. }
  1400. /*
  1401. * report nonzero exit code from a test process
  1402. */
  1403. static void
  1404. log_child_failure(int exitstatus)
  1405. {
  1406. if (WIFEXITED(exitstatus))
  1407. status(_(" (test process exited with exit code %d)"),
  1408. WEXITSTATUS(exitstatus));
  1409. else if (WIFSIGNALED(exitstatus))
  1410. {
  1411. #if defined(WIN32)
  1412. status(_(" (test process was terminated by exception 0x%X)"),
  1413. WTERMSIG(exitstatus));
  1414. #else
  1415. status(_(" (test process was terminated by signal %d: %s)"),
  1416. WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
  1417. #endif
  1418. }
  1419. else
  1420. status(_(" (test process exited with unrecognized status %d)"),
  1421. exitstatus);
  1422. }
  1423. /*
  1424. * Run all the tests specified in one schedule file
  1425. */
  1426. static void
  1427. run_schedule(const char *schedule, test_function tfunc)
  1428. {
  1429. #define MAX_PARALLEL_TESTS 100
  1430. char *tests[MAX_PARALLEL_TESTS];
  1431. _stringlist *resultfiles[MAX_PARALLEL_TESTS];
  1432. _stringlist *expectfiles[MAX_PARALLEL_TESTS];
  1433. _stringlist *tags[MAX_PARALLEL_TESTS];
  1434. PID_TYPE pids[MAX_PARALLEL_TESTS];
  1435. instr_time starttimes[MAX_PARALLEL_TESTS];
  1436. instr_time stoptimes[MAX_PARALLEL_TESTS];
  1437. int statuses[MAX_PARALLEL_TESTS];
  1438. _stringlist *ignorelist = NULL;
  1439. char scbuf[1024];
  1440. FILE *scf;
  1441. int line_num = 0;
  1442. memset(tests, 0, sizeof(tests));
  1443. memset(resultfiles, 0, sizeof(resultfiles));
  1444. memset(expectfiles, 0, sizeof(expectfiles));
  1445. memset(tags, 0, sizeof(tags));
  1446. scf = fopen(schedule, "r");
  1447. if (!scf)
  1448. {
  1449. fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
  1450. progname, schedule, strerror(errno));
  1451. exit(2);
  1452. }
  1453. while (fgets(scbuf, sizeof(scbuf), scf))
  1454. {
  1455. char *test = NULL;
  1456. char *c;
  1457. int num_tests;
  1458. bool inword;
  1459. int i;
  1460. line_num++;
  1461. /* strip trailing whitespace, especially the newline */
  1462. i = strlen(scbuf);
  1463. while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
  1464. scbuf[--i] = '\0';
  1465. if (scbuf[0] == '\0' || scbuf[0] == '#')
  1466. continue;
  1467. if (strncmp(scbuf, "test: ", 6) == 0)
  1468. test = scbuf + 6;
  1469. else if (strncmp(scbuf, "ignore: ", 8) == 0)
  1470. {
  1471. c = scbuf + 8;
  1472. while (*c && isspace((unsigned char) *c))
  1473. c++;
  1474. add_stringlist_item(&ignorelist, c);
  1475. /*
  1476. * Note: ignore: lines do not run the test, they just say that
  1477. * failure of this test when run later on is to be ignored. A bit
  1478. * odd but that's how the shell-script version did it.
  1479. */
  1480. continue;
  1481. }
  1482. else
  1483. {
  1484. fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
  1485. schedule, line_num, scbuf);
  1486. exit(2);
  1487. }
  1488. num_tests = 0;
  1489. inword = false;
  1490. for (c = test;; c++)
  1491. {
  1492. if (*c == '\0' || isspace((unsigned char) *c))
  1493. {
  1494. if (inword)
  1495. {
  1496. /* Reached end of a test name */
  1497. char sav;
  1498. if (num_tests >= MAX_PARALLEL_TESTS)
  1499. {
  1500. fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
  1501. MAX_PARALLEL_TESTS, schedule, line_num, scbuf);
  1502. exit(2);
  1503. }
  1504. sav = *c;
  1505. *c = '\0';
  1506. tests[num_tests] = pg_strdup(test);
  1507. num_tests++;
  1508. *c = sav;
  1509. inword = false;
  1510. }
  1511. if (*c == '\0')
  1512. break; /* loop exit is here */
  1513. }
  1514. else if (!inword)
  1515. {
  1516. /* Start of a test name */
  1517. test = c;
  1518. inword = true;
  1519. }
  1520. }
  1521. if (num_tests == 0)
  1522. {
  1523. fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
  1524. schedule, line_num, scbuf);
  1525. exit(2);
  1526. }
  1527. if (num_tests == 1)
  1528. {
  1529. status(_("test %-28s ... "), tests[0]);
  1530. pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
  1531. INSTR_TIME_SET_CURRENT(starttimes[0]);
  1532. wait_for_tests(pids, statuses, stoptimes, NULL, 1);
  1533. /* status line is finished below */
  1534. }
  1535. else if (max_concurrent_tests > 0 && max_concurrent_tests < num_tests)
  1536. {
  1537. fprintf(stderr, _("too many parallel tests (more than %d) in schedule file \"%s\" line %d: %s\n"),
  1538. max_concurrent_tests, schedule, line_num, scbuf);
  1539. exit(2);
  1540. }
  1541. else if (max_connections > 0 && max_connections < num_tests)
  1542. {
  1543. int oldest = 0;
  1544. status(_("parallel group (%d tests, in groups of %d): "),
  1545. num_tests, max_connections);
  1546. for (i = 0; i < num_tests; i++)
  1547. {
  1548. if (i - oldest >= max_connections)
  1549. {
  1550. wait_for_tests(pids + oldest, statuses + oldest,
  1551. stoptimes + oldest,
  1552. tests + oldest, i - oldest);
  1553. oldest = i;
  1554. }
  1555. pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
  1556. INSTR_TIME_SET_CURRENT(starttimes[i]);
  1557. }
  1558. wait_for_tests(pids + oldest, statuses + oldest,
  1559. stoptimes + oldest,
  1560. tests + oldest, i - oldest);
  1561. status_end();
  1562. }
  1563. else
  1564. {
  1565. status(_("parallel group (%d tests): "), num_tests);
  1566. for (i = 0; i < num_tests; i++)
  1567. {
  1568. pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
  1569. INSTR_TIME_SET_CURRENT(starttimes[i]);
  1570. }
  1571. wait_for_tests(pids, statuses, stoptimes, tests, num_tests);
  1572. status_end();
  1573. }
  1574. /* Check results for all tests */
  1575. for (i = 0; i < num_tests; i++)
  1576. {
  1577. _stringlist *rl,
  1578. *el,
  1579. *tl;
  1580. bool differ = false;
  1581. if (num_tests > 1)
  1582. status(_(" %-28s ... "), tests[i]);
  1583. /*
  1584. * Advance over all three lists simultaneously.
  1585. *
  1586. * Compare resultfiles[j] with expectfiles[j] always. Tags are
  1587. * optional but if there are tags, the tag list has the same
  1588. * length as the other two lists.
  1589. */
  1590. for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
  1591. rl != NULL; /* rl and el have the same length */
  1592. rl = rl->next, el = el->next,
  1593. tl = tl ? tl->next : NULL)
  1594. {
  1595. bool newdiff;
  1596. newdiff = results_differ(tests[i], rl->str, el->str);
  1597. if (newdiff && tl)
  1598. {
  1599. printf("%s ", tl->str);
  1600. }
  1601. differ |= newdiff;
  1602. }
  1603. if (differ)
  1604. {
  1605. bool ignore = false;
  1606. _stringlist *sl;
  1607. for (sl = ignorelist; sl != NULL; sl = sl->next)
  1608. {
  1609. if (strcmp(tests[i], sl->str) == 0)
  1610. {
  1611. ignore = true;
  1612. break;
  1613. }
  1614. }
  1615. if (ignore)
  1616. {
  1617. status(_("failed (ignored)"));
  1618. fail_ignore_count++;
  1619. }
  1620. else
  1621. {
  1622. status(_("FAILED"));
  1623. fail_count++;
  1624. }
  1625. }
  1626. else
  1627. {
  1628. status(_("ok ")); /* align with FAILED */
  1629. success_count++;
  1630. }
  1631. if (statuses[i] != 0)
  1632. log_child_failure(statuses[i]);
  1633. INSTR_TIME_SUBTRACT(stoptimes[i], starttimes[i]);
  1634. status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptimes[i]));
  1635. status_end();
  1636. }
  1637. for (i = 0; i < num_tests; i++)
  1638. {
  1639. pg_free(tests[i]);
  1640. tests[i] = NULL;
  1641. free_stringlist(&resultfiles[i]);
  1642. free_stringlist(&expectfiles[i]);
  1643. free_stringlist(&tags[i]);
  1644. }
  1645. }
  1646. free_stringlist(&ignorelist);
  1647. fclose(scf);
  1648. }
  1649. /*
  1650. * Run a single test
  1651. */
  1652. static void
  1653. run_single_test(const char *test, test_function tfunc)
  1654. {
  1655. PID_TYPE pid;
  1656. instr_time starttime;
  1657. instr_time stoptime;
  1658. int exit_status;
  1659. _stringlist *resultfiles = NULL;
  1660. _stringlist *expectfiles = NULL;
  1661. _stringlist *tags = NULL;
  1662. _stringlist *rl,
  1663. *el,
  1664. *tl;
  1665. bool differ = false;
  1666. status(_("test %-28s ... "), test);
  1667. pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
  1668. INSTR_TIME_SET_CURRENT(starttime);
  1669. wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1);
  1670. /*
  1671. * Advance over all three lists simultaneously.
  1672. *
  1673. * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
  1674. * but if there are tags, the tag list has the same length as the other
  1675. * two lists.
  1676. */
  1677. for (rl = resultfiles, el = expectfiles, tl = tags;
  1678. rl != NULL; /* rl and el have the same length */
  1679. rl = rl->next, el = el->next,
  1680. tl = tl ? tl->next : NULL)
  1681. {
  1682. bool newdiff;
  1683. newdiff = results_differ(test, rl->str, el->str);
  1684. if (newdiff && tl)
  1685. {
  1686. printf("%s ", tl->str);
  1687. }
  1688. differ |= newdiff;
  1689. }
  1690. if (differ)
  1691. {
  1692. status(_("FAILED"));
  1693. fail_count++;
  1694. }
  1695. else
  1696. {
  1697. status(_("ok ")); /* align with FAILED */
  1698. success_count++;
  1699. }
  1700. if (exit_status != 0)
  1701. log_child_failure(exit_status);
  1702. INSTR_TIME_SUBTRACT(stoptime, starttime);
  1703. status(_(" %8.0f ms"), INSTR_TIME_GET_MILLISEC(stoptime));
  1704. status_end();
  1705. }
  1706. /*
  1707. * Create the summary-output files (making them empty if already existing)
  1708. */
  1709. static void
  1710. open_result_files(void)
  1711. {
  1712. char file[MAXPGPATH];
  1713. FILE *difffile;
  1714. /* create outputdir directory if not present */
  1715. if (!directory_exists(outputdir))
  1716. make_directory(outputdir);
  1717. /* create the log file (copy of running status output) */
  1718. snprintf(file, sizeof(file), "%s/regression.out", outputdir);
  1719. logfilename = pg_strdup(file);
  1720. logfile = fopen(logfilename, "w");
  1721. if (!logfile)
  1722. {
  1723. fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  1724. progname, logfilename, strerror(errno));
  1725. exit(2);
  1726. }
  1727. /* create the diffs file as empty */
  1728. snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
  1729. difffilename = pg_strdup(file);
  1730. difffile = fopen(difffilename, "w");
  1731. if (!difffile)
  1732. {
  1733. fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
  1734. progname, difffilename, strerror(errno));
  1735. exit(2);
  1736. }
  1737. /* we don't keep the diffs file open continuously */
  1738. fclose(difffile);
  1739. /* also create the results directory if not present */
  1740. snprintf(file, sizeof(file), "%s/results", outputdir);
  1741. if (!directory_exists(file))
  1742. make_directory(file);
  1743. }
  1744. static void
  1745. drop_database_if_exists(const char *dbname)
  1746. {
  1747. header(_("dropping database \"%s\""), dbname);
  1748. psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
  1749. }
  1750. static void
  1751. create_database(const char *dbname)
  1752. {
  1753. _stringlist *sl;
  1754. /*
  1755. * We use template0 so that any installation-local cruft in template1 will
  1756. * not mess up the tests.
  1757. */
  1758. header(_("creating database \"%s\""), dbname);
  1759. if (encoding)
  1760. psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
  1761. (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
  1762. else
  1763. psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
  1764. (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
  1765. psql_command(dbname,
  1766. "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
  1767. "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
  1768. "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
  1769. "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
  1770. "ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
  1771. "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
  1772. dbname, dbname, dbname, dbname, dbname, dbname);
  1773. /*
  1774. * Install any requested extensions. We use CREATE IF NOT EXISTS so that
  1775. * this will work whether or not the extension is preinstalled.
  1776. */
  1777. for (sl = loadextension; sl != NULL; sl = sl->next)
  1778. {
  1779. header(_("installing %s"), sl->str);
  1780. psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
  1781. }
  1782. }
  1783. static void
  1784. drop_role_if_exists(const char *rolename)
  1785. {
  1786. header(_("dropping role \"%s\""), rolename);
  1787. psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
  1788. }
  1789. static void
  1790. create_role(const char *rolename, const _stringlist *granted_dbs)
  1791. {
  1792. header(_("creating role \"%s\""), rolename);
  1793. psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
  1794. for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
  1795. {
  1796. psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
  1797. granted_dbs->str, rolename);
  1798. }
  1799. }
  1800. static void
  1801. help(void)
  1802. {
  1803. printf(_("PostgreSQL regression test driver\n"));
  1804. printf(_("\n"));
  1805. printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
  1806. printf(_("\n"));
  1807. printf(_("Options:\n"));
  1808. printf(_(" --bindir=BINPATH use BINPATH for programs that are run;\n"));
  1809. printf(_(" if empty, use PATH from the environment\n"));
  1810. printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
  1811. printf(_(" --create-role=ROLE create the specified role before testing\n"));
  1812. printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
  1813. printf(_(" --debug …

Large files files are truncated, but you can click here to view the full file