~ubuntu-branches/ubuntu/trusty/postgresql-9.3/trusty-proposed

« back to all changes in this revision

Viewing changes to .pc/60-pg_regress_socketdir.patch/src/test/regress/pg_regress.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2014-07-24 16:13:59 UTC
  • mfrom: (1.1.8)
  • Revision ID: package-import@ubuntu.com-20140724161359-uk325qfv03euxuuh
Tags: 9.3.5-0ubuntu0.14.04.1
* New upstream bug fix release: (LP: #1348176)
  - pg_upgrade: Users who upgraded to version 9.3 using pg_upgrade may have
    an issue with transaction information which causes VACUUM to eventually
    fail. These users should run the script provided in the release notes to
    determine if their installation is affected, and then take the remedy
    steps outlined there.
  - Various data integrity and other bug fixes.
  - Secure Unix-domain sockets of temporary postmasters started during make
    check.
    Any local user able to access the socket file could connect as the
    server's bootstrap superuser, then proceed to execute arbitrary code as
    the operating-system user running the test, as we previously noted in
    CVE-2014-0067. This change defends against that risk by placing the
    server's socket in a temporary, mode 0700 subdirectory of /tmp.
  - See release notes for details:
    http://www.postgresql.org/about/news/1534/
* Remove pg_regress patches to support --host=/path, obsolete with above
  upstream changes and not applicable any more.
* Drop tcl8.6 patch, applied upstream.
* Add missing logrotate test dependency.

Show diffs side-by-side

added added

removed removed

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