Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions | Variables

pg_regress.c File Reference

#include "pg_regress.h"
#include <ctype.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include "getopt_long.h"
#include "pg_config_paths.h"
Include dependency graph for pg_regress.c:

Go to the source code of this file.

Data Structures

struct  _resultmap

Defines

#define MAX_PARALLEL_TESTS   100
#define ULONGPID(x)   (unsigned long) (x)

Typedefs

typedef struct _resultmap _resultmap

Functions

static bool directory_exists (const char *dir)
static void make_directory (const char *dir)
static void header (const char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE
static void static void status (const char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE
static void static void static void psql_command (const char *database, const char *query,...) __attribute__((format(PG_PRINTF_ATTRIBUTE
static void static void static
void void 
add_stringlist_item (_stringlist **listhead, const char *str)
static void free_stringlist (_stringlist **listhead)
static void split_to_stringlist (const char *s, const char *delim, _stringlist **listhead)
static void status_end (void)
static void stop_postmaster (void)
static bool string_matches_pattern (const char *str, const char *pattern)
void replace_string (char *string, char *replace, char *replacement)
static void convert_sourcefiles_in (char *source_subdir, char *dest_dir, char *dest_subdir, char *suffix)
static void convert_sourcefiles (void)
static void load_resultmap (void)
static const char * get_expectfile (const char *testname, const char *file)
static void doputenv (const char *var, const char *val)
static void add_to_path (const char *pathname, char separator, const char *addval)
static void initialize_environment (void)
PID_TYPE spawn_process (const char *cmdline)
static long file_size (const char *file)
static int file_line_count (const char *file)
bool file_exists (const char *file)
static char * get_alternative_expectfile (const char *expectfile, int i)
static int run_diff (const char *cmd, const char *filename)
static bool results_differ (const char *testname, const char *resultsfile, const char *default_expectfile)
static void wait_for_tests (PID_TYPE *pids, int *statuses, char **names, int num_tests)
static void log_child_failure (int exitstatus)
static void run_schedule (const char *schedule, test_function tfunc)
static void run_single_test (const char *test, test_function tfunc)
static void open_result_files (void)
static void drop_database_if_exists (const char *dbname)
static void create_database (const char *dbname)
static void drop_role_if_exists (const char *rolename)
static void create_role (const char *rolename, const _stringlist *granted_dbs)
static char * make_absolute_path (const char *in)
static void help (void)
int regression_main (int argc, char *argv[], init_function ifunc, test_function tfunc)

Variables

char * bindir = PGBINDIR
char * libdir = LIBDIR
char * datadir = PGSHAREDIR
char * host_platform = HOST_TUPLE
static char * makeprog = MAKEPROG
static char * shellprog = SHELLPROG
const char * basic_diff_opts = ""
const char * pretty_diff_opts = "-C3"
_stringlistdblist = NULL
bool debug = false
char * inputdir = "."
char * outputdir = "."
char * psqldir = PGBINDIR
char * launcher = NULL
static _stringlistloadlanguage = NULL
static _stringlistloadextension = NULL
static int max_connections = 0
static char * encoding = NULL
static _stringlistschedulelist = NULL
static _stringlistextra_tests = NULL
static char * temp_install = NULL
static char * temp_config = NULL
static char * top_builddir = NULL
static bool nolocale = false
static bool use_existing = false
static char * hostname = NULL
static int port = -1
static bool port_specified_by_user = false
static char * dlpath = PKGLIBDIR
static char * user = NULL
static _stringlistextraroles = NULL
static _stringlistextra_install = NULL
static const char * progname
static char * logfilename
static FILE * logfile
static char * difffilename
static _resultmapresultmap = NULL
static PID_TYPE postmaster_pid = INVALID_PID
static bool postmaster_running = false
static int success_count = 0
static int fail_count = 0
static int fail_ignore_count = 0

Define Documentation

#define MAX_PARALLEL_TESTS   100

Referenced by run_schedule().

#define ULONGPID (   x  )     (unsigned long) (x)

Referenced by regression_main().


Typedef Documentation

typedef struct _resultmap _resultmap

Function Documentation

static void static void static void void add_stringlist_item ( _stringlist **  listhead,
const char *  str 
)

Definition at line 180 of file pg_regress.c.

References malloc, _stringlist::next, NULL, and _stringlist::str.

Referenced by ecpg_start_test(), isolation_init(), isolation_start_test(), psql_init(), psql_start_test(), regression_main(), run_schedule(), and split_to_stringlist().

{
    _stringlist *newentry = malloc(sizeof(_stringlist));
    _stringlist *oldentry;

    newentry->str = strdup(str);
    newentry->next = NULL;
    if (*listhead == NULL)
        *listhead = newentry;
    else
    {
        for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
             /* skip */ ;
        oldentry->next = newentry;
    }
}

static void add_to_path ( const char *  pathname,
char  separator,
const char *  addval 
) [static]

Definition at line 668 of file pg_regress.c.

References malloc, and putenv.

Referenced by initialize_environment().

{
    char       *oldval = getenv(pathname);
    char       *newval;

    if (!oldval || !oldval[0])
    {
        /* no previous value */
        newval = malloc(strlen(pathname) + strlen(addval) + 2);
        sprintf(newval, "%s=%s", pathname, addval);
    }
    else
    {
        newval = malloc(strlen(pathname) + strlen(addval) + strlen(oldval) + 3);
        sprintf(newval, "%s=%s%c%s", pathname, addval, separator, oldval);
    }
    putenv(newval);
}

static void convert_sourcefiles ( void   )  [static]

Definition at line 524 of file pg_regress.c.

References convert_sourcefiles_in(), inputdir, and outputdir.

Referenced by initialize_environment().

{
    convert_sourcefiles_in("input", inputdir, "sql", "sql");
    convert_sourcefiles_in("output", outputdir, "expected", "out");
}

static void convert_sourcefiles_in ( char *  source_subdir,
char *  dest_dir,
char *  dest_subdir,
char *  suffix 
) [static]

Definition at line 410 of file pg_regress.c.

References _, directory_exists(), dlpath, inputdir, make_directory(), MAXPGPATH, name, outputdir, pgfnames(), pgfnames_cleanup(), progname, replace_string(), rmtree(), snprintf(), and strerror().

Referenced by convert_sourcefiles().

{
    char        testtablespace[MAXPGPATH];
    char        indir[MAXPGPATH];
    struct stat st;
    int         ret;
    char      **name;
    char      **names;
    int         count = 0;

    snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir);

    /* Check that indir actually exists and is a directory */
    ret = stat(indir, &st);
    if (ret != 0 || !S_ISDIR(st.st_mode))
    {
        /*
         * No warning, to avoid noise in tests that do not have these
         * directories; for example, ecpg, contrib and src/pl.
         */
        return;
    }

    names = pgfnames(indir);
    if (!names)
        /* Error logged in pgfnames */
        exit(2);

    snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);

#ifdef WIN32

    /*
     * On Windows only, clean out the test tablespace dir, or create it if it
     * doesn't exist.  On other platforms we expect the Makefile to take care
     * of that.  (We don't migrate that functionality in here because it'd be
     * harder to cope with platform-specific issues such as SELinux.)
     *
     * XXX it would be better if pg_regress.c had nothing at all to do with
     * testtablespace, and this were handled by a .BAT file or similar on
     * Windows.  See pgsql-hackers discussion of 2008-01-18.
     */
    if (directory_exists(testtablespace))
        rmtree(testtablespace, true);
    make_directory(testtablespace);
#endif

    /* finally loop on each file and do the replacement */
    for (name = names; *name; name++)
    {
        char        srcfile[MAXPGPATH];
        char        destfile[MAXPGPATH];
        char        prefix[MAXPGPATH];
        FILE       *infile,
                   *outfile;
        char        line[1024];

        /* reject filenames not finishing in ".source" */
        if (strlen(*name) < 8)
            continue;
        if (strcmp(*name + strlen(*name) - 7, ".source") != 0)
            continue;

        count++;

        /* build the full actual paths to open */
        snprintf(prefix, strlen(*name) - 6, "%s", *name);
        snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name);
        snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir,
                 prefix, suffix);

        infile = fopen(srcfile, "r");
        if (!infile)
        {
            fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
                    progname, srcfile, strerror(errno));
            exit(2);
        }
        outfile = fopen(destfile, "w");
        if (!outfile)
        {
            fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
                    progname, destfile, strerror(errno));
            exit(2);
        }
        while (fgets(line, sizeof(line), infile))
        {
            replace_string(line, "@abs_srcdir@", inputdir);
            replace_string(line, "@abs_builddir@", outputdir);
            replace_string(line, "@testtablespace@", testtablespace);
            replace_string(line, "@libdir@", dlpath);
            replace_string(line, "@DLSUFFIX@", DLSUFFIX);
            fputs(line, outfile);
        }
        fclose(infile);
        fclose(outfile);
    }

    /*
     * If we didn't process any files, complain because it probably means
     * somebody neglected to pass the needed --inputdir argument.
     */
    if (count <= 0)
    {
        fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"),
                progname, indir);
        exit(2);
    }

    pgfnames_cleanup(names);
}

static void create_database ( const char *  dbname  )  [static]

Definition at line 1782 of file pg_regress.c.

References _, encoding, header(), _stringlist::next, nolocale, psql_command(), and _stringlist::str.

Referenced by regression_main().

{
    _stringlist *sl;

    /*
     * We use template0 so that any installation-local cruft in template1 will
     * not mess up the tests.
     */
    header(_("creating database \"%s\""), dbname);
    if (encoding)
        psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
                     (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
    else
        psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
                     (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
    psql_command(dbname,
                 "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
                 "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
                 "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
                 "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
            "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
                 dbname, dbname, dbname, dbname, dbname);

    /*
     * Install any requested procedural languages.  We use CREATE OR REPLACE
     * so that this will work whether or not the language is preinstalled.
     */
    for (sl = loadlanguage; sl != NULL; sl = sl->next)
    {
        header(_("installing %s"), sl->str);
        psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
    }

    /*
     * Install any requested extensions.  We use CREATE IF NOT EXISTS so that
     * this will work whether or not the extension is preinstalled.
     */
    for (sl = loadextension; sl != NULL; sl = sl->next)
    {
        header(_("installing %s"), sl->str);
        psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
    }
}

static void create_role ( const char *  rolename,
const _stringlist granted_dbs 
) [static]

Definition at line 1834 of file pg_regress.c.

References _, header(), _stringlist::next, psql_command(), and _stringlist::str.

Referenced by regression_main().

{
    header(_("creating role \"%s\""), rolename);
    psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
    for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
    {
        psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
                     granted_dbs->str, rolename);
    }
}

static bool directory_exists ( const char *  dir  )  [static]

Definition at line 1133 of file pg_regress.c.

Referenced by convert_sourcefiles_in(), open_result_files(), and regression_main().

{
    struct stat st;

    if (stat(dir, &st) != 0)
        return false;
    if (S_ISDIR(st.st_mode))
        return true;
    return false;
}

static void doputenv ( const char *  var,
const char *  val 
) [static]

Definition at line 655 of file pg_regress.c.

References malloc, and putenv.

Referenced by initialize_environment(), and regression_main().

{
    char       *s = malloc(strlen(var) + strlen(val) + 2);

    sprintf(s, "%s=%s", var, val);
    putenv(s);
}

static void drop_database_if_exists ( const char *  dbname  )  [static]

Definition at line 1775 of file pg_regress.c.

References _, header(), and psql_command().

Referenced by regression_main().

{
    header(_("dropping database \"%s\""), dbname);
    psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
}

static void drop_role_if_exists ( const char *  rolename  )  [static]

Definition at line 1827 of file pg_regress.c.

References _, header(), and psql_command().

Referenced by regression_main().

{
    header(_("dropping role \"%s\""), rolename);
    psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
}

bool file_exists ( const char *  file  ) 

Definition at line 1122 of file pg_regress.c.

{
    FILE       *f = fopen(file, "r");

    if (!f)
        return false;
    fclose(f);
    return true;
}

static int file_line_count ( const char *  file  )  [static]

Definition at line 1100 of file pg_regress.c.

References _, progname, and strerror().

Referenced by results_differ().

{
    int         c;
    int         l = 0;
    FILE       *f = fopen(file, "r");

    if (!f)
    {
        fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
                progname, file, strerror(errno));
        return -1;
    }
    while ((c = fgetc(f)) != EOF)
    {
        if (c == '\n')
            l++;
    }
    fclose(f);
    return l;
}

static long file_size ( const char *  file  )  [static]

Definition at line 1079 of file pg_regress.c.

References _, progname, and strerror().

Referenced by regression_main(), and run_diff().

{
    long        r;
    FILE       *f = fopen(file, "r");

    if (!f)
    {
        fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
                progname, file, strerror(errno));
        return -1;
    }
    fseek(f, 0, SEEK_END);
    r = ftell(f);
    fclose(f);
    return r;
}

static void free_stringlist ( _stringlist **  listhead  )  [static]

Definition at line 201 of file pg_regress.c.

References free, and NULL.

Referenced by regression_main(), and run_schedule().

{
    if (listhead == NULL || *listhead == NULL)
        return;
    if ((*listhead)->next != NULL)
        free_stringlist(&((*listhead)->next));
    free((*listhead)->str);
    free(*listhead);
    *listhead = NULL;
}

static char* get_alternative_expectfile ( const char *  expectfile,
int  i 
) [static]

Definition at line 1160 of file pg_regress.c.

References free, malloc, and snprintf().

Referenced by results_differ().

{
    char       *last_dot;
    int         ssize = strlen(expectfile) + 2 + 1;
    char       *tmp = (char *) malloc(ssize);
    char       *s = (char *) malloc(ssize);

    strcpy(tmp, expectfile);
    last_dot = strrchr(tmp, '.');
    if (!last_dot)
    {
        free(tmp);
        free(s);
        return NULL;
    }
    *last_dot = '\0';
    snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
    free(tmp);
    return s;
}

static const char* get_expectfile ( const char *  testname,
const char *  file 
) [static]

Definition at line 626 of file pg_regress.c.

References _resultmap::next, _resultmap::resultfile, _resultmap::test, and _resultmap::type.

Referenced by results_differ().

{
    char       *file_type;
    _resultmap *rm;

    /*
     * Determine the file type from the file name. This is just what is
     * following the last dot in the file name.
     */
    if (!file || !(file_type = strrchr(file, '.')))
        return NULL;

    file_type++;

    for (rm = resultmap; rm != NULL; rm = rm->next)
    {
        if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
        {
            return rm->resultfile;
        }
    }

    return NULL;
}

static void header ( const char *  fmt,
  ... 
) [static]

Definition at line 233 of file pg_regress.c.

References vsnprintf().

Referenced by _tarPositionTo(), create_database(), create_role(), decodePageSplitRecord(), drop_database_if_exists(), drop_role_if_exists(), ExecHashJoinGetSavedTuple(), ReceiveTarFile(), and regression_main().

{
    char        tmp[64];
    va_list     ap;

    va_start(ap, fmt);
    vsnprintf(tmp, sizeof(tmp), fmt, ap);
    va_end(ap);

    fprintf(stdout, "============== %-38s ==============\n", tmp);
    fflush(stdout);
}

static void help ( void   )  [static]

Definition at line 1874 of file pg_regress.c.

References _, and progname.

Referenced by regression_main().

{
    printf(_("PostgreSQL regression test driver\n"));
    printf(_("\n"));
    printf(_("Usage:\n  %s [OPTION]... [EXTRA-TEST]...\n"), progname);
    printf(_("\n"));
    printf(_("Options:\n"));
    printf(_("  --create-role=ROLE        create the specified role before testing\n"));
    printf(_("  --dbname=DB               use database DB (default \"regression\")\n"));
    printf(_("  --debug                   turn on debug mode in programs that are run\n"));
    printf(_("  --dlpath=DIR              look for dynamic libraries in DIR\n"));
    printf(_("  --encoding=ENCODING       use ENCODING as the encoding\n"));
    printf(_("  --inputdir=DIR            take input files from DIR (default \".\")\n"));
    printf(_("  --launcher=CMD            use CMD as launcher of psql\n"));
    printf(_("  --load-extension=EXT      load the named extension before running the\n"));
    printf(_("                            tests; can appear multiple times\n"));
    printf(_("  --load-language=LANG      load the named language before running the\n"));
    printf(_("                            tests; can appear multiple times\n"));
    printf(_("  --max-connections=N       maximum number of concurrent connections\n"));
    printf(_("                            (default is 0, meaning unlimited)\n"));
    printf(_("  --outputdir=DIR           place output files in DIR (default \".\")\n"));
    printf(_("  --schedule=FILE           use test ordering schedule from FILE\n"));
    printf(_("                            (can be used multiple times to concatenate)\n"));
    printf(_("  --temp-install=DIR        create a temporary installation in DIR\n"));
    printf(_("  --use-existing            use an existing installation\n"));
    printf(_("\n"));
    printf(_("Options for \"temp-install\" mode:\n"));
    printf(_("  --extra-install=DIR       additional directory to install (e.g., contrib)\n"));
    printf(_("  --no-locale               use C locale\n"));
    printf(_("  --port=PORT               start postmaster on PORT\n"));
    printf(_("  --temp-config=FILE        append contents of FILE to temporary config\n"));
    printf(_("  --top-builddir=DIR        (relative) path to top level build directory\n"));
    printf(_("\n"));
    printf(_("Options for using an existing installation:\n"));
    printf(_("  --host=HOST               use postmaster running on HOST\n"));
    printf(_("  --port=PORT               use postmaster running at PORT\n"));
    printf(_("  --user=USER               connect as USER\n"));
    printf(_("  --psqldir=DIR             use psql in DIR (default: configured bindir)\n"));
    printf(_("\n"));
    printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
    printf(_("if the tests could not be run for some reason.\n"));
    printf(_("\n"));
    printf(_("Report bugs to <[email protected]>.\n"));
}

static void initialize_environment ( void   )  [static]

Definition at line 691 of file pg_regress.c.

References _, add_to_path(), bindir, convert_sourcefiles(), datadir, doputenv(), encoding, hostname, libdir, load_resultmap(), malloc, nolocale, NULL, pghost, pgport, port, psqldir, putenv, temp_install, unsetenv, and user.

Referenced by regression_main().

{
    char       *tmp;

    putenv("PGAPPNAME=pg_regress");

    if (nolocale)
    {
        /*
         * Clear out any non-C locale settings
         */
        unsetenv("LC_COLLATE");
        unsetenv("LC_CTYPE");
        unsetenv("LC_MONETARY");
        unsetenv("LC_NUMERIC");
        unsetenv("LC_TIME");
        unsetenv("LANG");
        /* On Windows the default locale cannot be English, so force it */
#if defined(WIN32) || defined(__CYGWIN__)
        putenv("LANG=en");
#endif
    }

    /*
     * Set translation-related settings to English; otherwise psql will
     * produce translated messages and produce diffs.  (XXX If we ever support
     * translation of pg_regress, this needs to be moved elsewhere, where psql
     * is actually called.)
     */
    unsetenv("LANGUAGE");
    unsetenv("LC_ALL");
    putenv("LC_MESSAGES=C");

    /*
     * Set encoding as requested
     */
    if (encoding)
        doputenv("PGCLIENTENCODING", encoding);
    else
        unsetenv("PGCLIENTENCODING");

    /*
     * Set timezone and datestyle for datetime-related tests
     */
    putenv("PGTZ=PST8PDT");
    putenv("PGDATESTYLE=Postgres, MDY");

    /*
     * Likewise set intervalstyle to ensure consistent results.  This is a bit
     * more painful because we must use PGOPTIONS, and we want to preserve the
     * user's ability to set other variables through that.
     */
    {
        const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
        const char *old_pgoptions = getenv("PGOPTIONS");
        char       *new_pgoptions;

        if (!old_pgoptions)
            old_pgoptions = "";
        new_pgoptions = malloc(strlen(old_pgoptions) + strlen(my_pgoptions) + 12);
        sprintf(new_pgoptions, "PGOPTIONS=%s %s", old_pgoptions, my_pgoptions);
        putenv(new_pgoptions);
    }

    if (temp_install)
    {
        /*
         * Clear out any environment vars that might cause psql to connect to
         * the wrong postmaster, or otherwise behave in nondefault ways. (Note
         * we also use psql's -X switch consistently, so that ~/.psqlrc files
         * won't mess things up.)  Also, set PGPORT to the temp port, and set
         * or unset PGHOST depending on whether we are using TCP or Unix
         * sockets.
         */
        unsetenv("PGDATABASE");
        unsetenv("PGUSER");
        unsetenv("PGSERVICE");
        unsetenv("PGSSLMODE");
        unsetenv("PGREQUIRESSL");
        unsetenv("PGCONNECT_TIMEOUT");
        unsetenv("PGDATA");
        if (hostname != NULL)
            doputenv("PGHOST", hostname);
        else
            unsetenv("PGHOST");
        unsetenv("PGHOSTADDR");
        if (port != -1)
        {
            char        s[16];

            sprintf(s, "%d", port);
            doputenv("PGPORT", s);
        }

        /*
         * GNU make stores some flags in the MAKEFLAGS environment variable to
         * pass arguments to its own children.  If we are invoked by make,
         * that causes the make invoked by us to think its part of the make
         * task invoking us, and so it tries to communicate with the toplevel
         * make.  Which fails.
         *
         * Unset the variable to protect against such problems.  We also reset
         * MAKELEVEL to be certain the child doesn't notice the make above us.
         */
        unsetenv("MAKEFLAGS");
        unsetenv("MAKELEVEL");

        /*
         * Adjust path variables to point into the temp-install tree
         */
        tmp = malloc(strlen(temp_install) + 32 + strlen(bindir));
        sprintf(tmp, "%s/install/%s", temp_install, bindir);
        bindir = tmp;

        tmp = malloc(strlen(temp_install) + 32 + strlen(libdir));
        sprintf(tmp, "%s/install/%s", temp_install, libdir);
        libdir = tmp;

        tmp = malloc(strlen(temp_install) + 32 + strlen(datadir));
        sprintf(tmp, "%s/install/%s", temp_install, datadir);
        datadir = tmp;

        /* psql will be installed into temp-install bindir */
        psqldir = bindir;

        /*
         * Set up shared library paths to include the temp install.
         *
         * LD_LIBRARY_PATH covers many platforms.  DYLD_LIBRARY_PATH works on
         * Darwin, and maybe other Mach-based systems.  LIBPATH is for AIX.
         * Windows needs shared libraries in PATH (only those linked into
         * executables, not dlopen'ed ones). Feel free to account for others
         * as well.
         */
        add_to_path("LD_LIBRARY_PATH", ':', libdir);
        add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
        add_to_path("LIBPATH", ':', libdir);
#if defined(WIN32)
        add_to_path("PATH", ';', libdir);
#elif defined(__CYGWIN__)
        add_to_path("PATH", ':', libdir);
#endif
    }
    else
    {
        const char *pghost;
        const char *pgport;

        /*
         * When testing an existing install, we honor existing environment
         * variables, except if they're overridden by command line options.
         */
        if (hostname != NULL)
        {
            doputenv("PGHOST", hostname);
            unsetenv("PGHOSTADDR");
        }
        if (port != -1)
        {
            char        s[16];

            sprintf(s, "%d", port);
            doputenv("PGPORT", s);
        }
        if (user != NULL)
            doputenv("PGUSER", user);

        /*
         * Report what we're connecting to
         */
        pghost = getenv("PGHOST");
        pgport = getenv("PGPORT");
#ifndef HAVE_UNIX_SOCKETS
        if (!pghost)
            pghost = "localhost";
#endif

        if (pghost && pgport)
            printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport);
        if (pghost && !pgport)
            printf(_("(using postmaster on %s, default port)\n"), pghost);
        if (!pghost && pgport)
            printf(_("(using postmaster on Unix socket, port %s)\n"), pgport);
        if (!pghost && !pgport)
            printf(_("(using postmaster on Unix socket, default port)\n"));
    }

    convert_sourcefiles();
    load_resultmap();
}

static void load_resultmap ( void   )  [static]

Definition at line 544 of file pg_regress.c.

References _, buf, host_platform, i, inputdir, malloc, _resultmap::next, progname, _resultmap::resultfile, snprintf(), strerror(), string_matches_pattern(), _resultmap::test, and _resultmap::type.

Referenced by initialize_environment().

{
    char        buf[MAXPGPATH];
    FILE       *f;

    /* scan the file ... */
    snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
    f = fopen(buf, "r");
    if (!f)
    {
        /* OK if it doesn't exist, else complain */
        if (errno == ENOENT)
            return;
        fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
                progname, buf, strerror(errno));
        exit(2);
    }

    while (fgets(buf, sizeof(buf), f))
    {
        char       *platform;
        char       *file_type;
        char       *expected;
        int         i;

        /* strip trailing whitespace, especially the newline */
        i = strlen(buf);
        while (i > 0 && isspace((unsigned char) buf[i - 1]))
            buf[--i] = '\0';

        /* parse out the line fields */
        file_type = strchr(buf, ':');
        if (!file_type)
        {
            fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
                    buf);
            exit(2);
        }
        *file_type++ = '\0';

        platform = strchr(file_type, ':');
        if (!platform)
        {
            fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
                    buf);
            exit(2);
        }
        *platform++ = '\0';
        expected = strchr(platform, '=');
        if (!expected)
        {
            fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
                    buf);
            exit(2);
        }
        *expected++ = '\0';

        /*
         * if it's for current platform, save it in resultmap list. Note: by
         * adding at the front of the list, we ensure that in ambiguous cases,
         * the last match in the resultmap file is used. This mimics the
         * behavior of the old shell script.
         */
        if (string_matches_pattern(host_platform, platform))
        {
            _resultmap *entry = malloc(sizeof(_resultmap));

            entry->test = strdup(buf);
            entry->type = strdup(file_type);
            entry->resultfile = strdup(expected);
            entry->next = resultmap;
            resultmap = entry;
        }
    }
    fclose(f);
}

static void log_child_failure ( int  exitstatus  )  [static]

Definition at line 1424 of file pg_regress.c.

References _, status(), WEXITSTATUS, WIFEXITED, WIFSIGNALED, and WTERMSIG.

Referenced by run_schedule(), and run_single_test().

{
    if (WIFEXITED(exitstatus))
        status(_(" (test process exited with exit code %d)"),
               WEXITSTATUS(exitstatus));
    else if (WIFSIGNALED(exitstatus))
    {
#if defined(WIN32)
        status(_(" (test process was terminated by exception 0x%X)"),
               WTERMSIG(exitstatus));
#elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
        status(_(" (test process was terminated by signal %d: %s)"),
               WTERMSIG(exitstatus),
               WTERMSIG(exitstatus) < NSIG ?
               sys_siglist[WTERMSIG(exitstatus)] : "(unknown))");
#else
        status(_(" (test process was terminated by signal %d)"),
               WTERMSIG(exitstatus));
#endif
    }
    else
        status(_(" (test process exited with unrecognized status %d)"),
               exitstatus);
}

static char* make_absolute_path ( const char *  in  )  [static]

Definition at line 1846 of file pg_regress.c.

References _, canonicalize_path(), is_absolute_path, malloc, and strerror().

{
    char       *result;

    if (is_absolute_path(in))
        result = strdup(in);
    else
    {
        static char cwdbuf[MAXPGPATH];

        if (!cwdbuf[0])
        {
            if (!getcwd(cwdbuf, sizeof(cwdbuf)))
            {
                fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
                exit(2);
            }
        }

        result = malloc(strlen(cwdbuf) + strlen(in) + 2);
        sprintf(result, "%s/%s", cwdbuf, in);
    }

    canonicalize_path(result);
    return result;
}

static void make_directory ( const char *  dir  )  [static]

Definition at line 1146 of file pg_regress.c.

References _, mkdir, progname, S_IRWXG, S_IRWXO, and strerror().

Referenced by convert_sourcefiles_in(), open_result_files(), and regression_main().

{
    if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
    {
        fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
                progname, dir, strerror(errno));
        exit(2);
    }
}

static void open_result_files ( void   )  [static]

Definition at line 1739 of file pg_regress.c.

References _, difffilename, directory_exists(), logfile, logfilename, make_directory(), outputdir, progname, snprintf(), and strerror().

Referenced by regression_main().

{
    char        file[MAXPGPATH];
    FILE       *difffile;

    /* create the log file (copy of running status output) */
    snprintf(file, sizeof(file), "%s/regression.out", outputdir);
    logfilename = strdup(file);
    logfile = fopen(logfilename, "w");
    if (!logfile)
    {
        fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
                progname, logfilename, strerror(errno));
        exit(2);
    }

    /* create the diffs file as empty */
    snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
    difffilename = strdup(file);
    difffile = fopen(difffilename, "w");
    if (!difffile)
    {
        fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
                progname, difffilename, strerror(errno));
        exit(2);
    }
    /* we don't keep the diffs file open continuously */
    fclose(difffile);

    /* also create the output directory if not present */
    snprintf(file, sizeof(file), "%s/results", outputdir);
    if (!directory_exists(file))
        make_directory(file);
}

static void psql_command ( const char *  database,
const char *  query,
  ... 
) [static]

Definition at line 888 of file pg_regress.c.

References _, MAXPGPATH, psqldir, snprintf(), system(), SYSTEMQUOTE, and vsnprintf().

Referenced by create_database(), create_role(), drop_database_if_exists(), and drop_role_if_exists().

{
    char        query_formatted[1024];
    char        query_escaped[2048];
    char        psql_cmd[MAXPGPATH + 2048];
    va_list     args;
    char       *s;
    char       *d;

    /* Generate the query with insertion of sprintf arguments */
    va_start(args, query);
    vsnprintf(query_formatted, sizeof(query_formatted), query, args);
    va_end(args);

    /* Now escape any shell double-quote metacharacters */
    d = query_escaped;
    for (s = query_formatted; *s; s++)
    {
        if (strchr("\\\"$`", *s))
            *d++ = '\\';
        *d++ = *s;
    }
    *d = '\0';

    /* And now we can build and execute the shell command */
    snprintf(psql_cmd, sizeof(psql_cmd),
             SYSTEMQUOTE "\"%s%spsql\" -X -c \"%s\" \"%s\"" SYSTEMQUOTE,
             psqldir ? psqldir : "",
             psqldir ? "/" : "",
             query_escaped,
             database);

    if (system(psql_cmd) != 0)
    {
        /* psql probably already reported the error */
        fprintf(stderr, _("command failed: %s\n"), psql_cmd);
        exit(2);
    }
}

int regression_main ( int  argc,
char *  argv[],
init_function  ifunc,
test_function  tfunc 
)

Definition at line 1920 of file pg_regress.c.

References _, add_stringlist_item(), bindir, buf, create_database(), create_role(), datadir, debug, DEVNULL, difffilename, directory_exists(), dlpath, doputenv(), drop_database_if_exists(), drop_role_if_exists(), encoding, fail_count, fail_ignore_count, file_size(), free_stringlist(), get_progname(), getopt_long(), header(), help(), hostname, i, initialize_environment(), inputdir, INVALID_PID, launcher, logfile, logfilename, make_absolute_path(), make_directory(), makeprog, max_connections, MAXPGPATH, _stringlist::next, nolocale, NULL, open_result_files(), optarg, optind, outputdir, PG_TEXTDOMAIN, pg_usleep(), port, port_specified_by_user, postmaster_pid, postmaster_running, pretty_diff_opts, progname, psqldir, rmtree(), run_schedule(), run_single_test(), set_pglocale_pgservice(), SIGKILL, snprintf(), spawn_process(), split_to_stringlist(), stop_postmaster(), _stringlist::str, strerror(), success_count, system(), SYSTEMQUOTE, temp_config, temp_install, top_builddir, ULONGPID, unlink(), use_existing, and user.

Referenced by main().

{
    static struct option long_options[] = {
        {"help", no_argument, NULL, 'h'},
        {"version", no_argument, NULL, 'V'},
        {"dbname", required_argument, NULL, 1},
        {"debug", no_argument, NULL, 2},
        {"inputdir", required_argument, NULL, 3},
        {"load-language", required_argument, NULL, 4},
        {"max-connections", required_argument, NULL, 5},
        {"encoding", required_argument, NULL, 6},
        {"outputdir", required_argument, NULL, 7},
        {"schedule", required_argument, NULL, 8},
        {"temp-install", required_argument, NULL, 9},
        {"no-locale", no_argument, NULL, 10},
        {"top-builddir", required_argument, NULL, 11},
        {"host", required_argument, NULL, 13},
        {"port", required_argument, NULL, 14},
        {"user", required_argument, NULL, 15},
        {"psqldir", required_argument, NULL, 16},
        {"dlpath", required_argument, NULL, 17},
        {"create-role", required_argument, NULL, 18},
        {"temp-config", required_argument, NULL, 19},
        {"use-existing", no_argument, NULL, 20},
        {"launcher", required_argument, NULL, 21},
        {"load-extension", required_argument, NULL, 22},
        {"extra-install", required_argument, NULL, 23},
        {NULL, 0, NULL, 0}
    };

    _stringlist *sl;
    int         c;
    int         i;
    int         option_index;
    char        buf[MAXPGPATH * 4];
    char        buf2[MAXPGPATH * 4];

    progname = get_progname(argv[0]);
    set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));

    atexit(stop_postmaster);

#ifndef HAVE_UNIX_SOCKETS
    /* no unix domain sockets available, so change default */
    hostname = "localhost";
#endif

    /*
     * We call the initialization function here because that way we can set
     * default parameters and let them be overwritten by the commandline.
     */
    ifunc();

    if (getenv("PG_REGRESS_DIFF_OPTS"))
        pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");

    while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
    {
        switch (c)
        {
            case 'h':
                help();
                exit(0);
            case 'V':
                puts("pg_regress (PostgreSQL) " PG_VERSION);
                exit(0);
            case 1:

                /*
                 * If a default database was specified, we need to remove it
                 * before we add the specified one.
                 */
                free_stringlist(&dblist);
                split_to_stringlist(strdup(optarg), ", ", &dblist);
                break;
            case 2:
                debug = true;
                break;
            case 3:
                inputdir = strdup(optarg);
                break;
            case 4:
                add_stringlist_item(&loadlanguage, optarg);
                break;
            case 5:
                max_connections = atoi(optarg);
                break;
            case 6:
                encoding = strdup(optarg);
                break;
            case 7:
                outputdir = strdup(optarg);
                break;
            case 8:
                add_stringlist_item(&schedulelist, optarg);
                break;
            case 9:
                temp_install = make_absolute_path(optarg);
                break;
            case 10:
                nolocale = true;
                break;
            case 11:
                top_builddir = strdup(optarg);
                break;
            case 13:
                hostname = strdup(optarg);
                break;
            case 14:
                port = atoi(optarg);
                port_specified_by_user = true;
                break;
            case 15:
                user = strdup(optarg);
                break;
            case 16:
                /* "--psqldir=" should mean to use PATH */
                if (strlen(optarg))
                    psqldir = strdup(optarg);
                break;
            case 17:
                dlpath = strdup(optarg);
                break;
            case 18:
                split_to_stringlist(strdup(optarg), ", ", &extraroles);
                break;
            case 19:
                temp_config = strdup(optarg);
                break;
            case 20:
                use_existing = true;
                break;
            case 21:
                launcher = strdup(optarg);
                break;
            case 22:
                add_stringlist_item(&loadextension, optarg);
                break;
            case 23:
                add_stringlist_item(&extra_install, optarg);
                break;
            default:
                /* getopt_long already emitted a complaint */
                fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
                        progname);
                exit(2);
        }
    }

    /*
     * if we still have arguments, they are extra tests to run
     */
    while (argc - optind >= 1)
    {
        add_stringlist_item(&extra_tests, argv[optind]);
        optind++;
    }

    if (temp_install && !port_specified_by_user)

        /*
         * To reduce chances of interference with parallel installations, use
         * a port number starting in the private range (49152-65535)
         * calculated from the version number.
         */
        port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);

    inputdir = make_absolute_path(inputdir);
    outputdir = make_absolute_path(outputdir);
    dlpath = make_absolute_path(dlpath);

    /*
     * Initialization
     */
    open_result_files();

    initialize_environment();

#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
    unlimit_core_size();
#endif

    if (temp_install)
    {
        FILE       *pg_conf;
        _stringlist *sl;

        /*
         * Prepare the temp installation
         */
        if (!top_builddir)
        {
            fprintf(stderr, _("--top-builddir must be specified when using --temp-install\n"));
            exit(2);
        }

        if (directory_exists(temp_install))
        {
            header(_("removing existing temp installation"));
            rmtree(temp_install, true);
        }

        header(_("creating temporary installation"));

        /* make the temp install top directory */
        make_directory(temp_install);

        /* and a directory for log files */
        snprintf(buf, sizeof(buf), "%s/log", outputdir);
        if (!directory_exists(buf))
            make_directory(buf);

        /* "make install" */
#ifndef WIN32_ONLY_COMPILER
        snprintf(buf, sizeof(buf),
                 SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
                 makeprog, top_builddir, temp_install, outputdir);
#else
        snprintf(buf, sizeof(buf),
                 SYSTEMQUOTE "perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
                 top_builddir, temp_install, outputdir);
#endif
        if (system(buf))
        {
            fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
            exit(2);
        }

        for (sl = extra_install; sl != NULL; sl = sl->next)
        {
#ifndef WIN32_ONLY_COMPILER
            snprintf(buf, sizeof(buf),
                     SYSTEMQUOTE "\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
                   makeprog, top_builddir, sl->str, temp_install, outputdir);
#else
            fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n"), progname);
            exit(2);
#endif

            if (system(buf))
            {
                fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
                exit(2);
            }
        }

        /* initdb */
        header(_("initializing database system"));
        snprintf(buf, sizeof(buf),
                 SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean --nosync%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
                 bindir, temp_install, datadir,
                 debug ? " --debug" : "",
                 nolocale ? " --no-locale" : "",
                 outputdir);
        if (system(buf))
        {
            fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
            exit(2);
        }

        /*
         * Adjust the default postgresql.conf as needed for regression
         * testing. The user can specify a file to be appended; in any case we
         * set max_prepared_transactions to enable testing of prepared xacts.
         * (Note: to reduce the probability of unexpected shmmax failures,
         * don't set max_prepared_transactions any higher than actually needed
         * by the prepared_xacts regression test.)
         */
        snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install);
        pg_conf = fopen(buf, "a");
        if (pg_conf == NULL)
        {
            fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
            exit(2);
        }
        fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
        fputs("max_prepared_transactions = 2\n", pg_conf);

        if (temp_config != NULL)
        {
            FILE       *extra_conf;
            char        line_buf[1024];

            extra_conf = fopen(temp_config, "r");
            if (extra_conf == NULL)
            {
                fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno));
                exit(2);
            }
            while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
                fputs(line_buf, pg_conf);
            fclose(extra_conf);
        }

        fclose(pg_conf);

        /*
         * Check if there is a postmaster running already.
         */
        snprintf(buf2, sizeof(buf2),
                 SYSTEMQUOTE "\"%s/psql\" -X postgres <%s 2>%s" SYSTEMQUOTE,
                 bindir, DEVNULL, DEVNULL);

        for (i = 0; i < 16; i++)
        {
            if (system(buf2) == 0)
            {
                char        s[16];

                if (port_specified_by_user || i == 15)
                {
                    fprintf(stderr, _("port %d apparently in use\n"), port);
                    if (!port_specified_by_user)
                        fprintf(stderr, _("%s: could not determine an available port\n"), progname);
                    fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n"));
                    exit(2);
                }

                fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1);
                port++;
                sprintf(s, "%d", port);
                doputenv("PGPORT", s);
            }
            else
                break;
        }

        /*
         * Start the temp postmaster
         */
        header(_("starting postmaster"));
        snprintf(buf, sizeof(buf),
                 SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s -c \"listen_addresses=%s\" > \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE,
                 bindir, temp_install,
                 debug ? " -d 5" : "",
                 hostname ? hostname : "",
                 outputdir);
        postmaster_pid = spawn_process(buf);
        if (postmaster_pid == INVALID_PID)
        {
            fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"),
                    progname, strerror(errno));
            exit(2);
        }

        /*
         * Wait till postmaster is able to accept connections (normally only a
         * second or so, but Cygwin is reportedly *much* slower).  Don't wait
         * forever, however.
         */
        for (i = 0; i < 60; i++)
        {
            /* Done if psql succeeds */
            if (system(buf2) == 0)
                break;

            /*
             * Fail immediately if postmaster has exited
             */
#ifndef WIN32
            if (kill(postmaster_pid, 0) != 0)
#else
            if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
#endif
            {
                fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
                exit(2);
            }

            pg_usleep(1000000L);
        }
        if (i >= 60)
        {
            fprintf(stderr, _("\n%s: postmaster did not respond within 60 seconds\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);

            /*
             * If we get here, the postmaster is probably wedged somewhere in
             * startup.  Try to kill it ungracefully rather than leaving a
             * stuck postmaster that might interfere with subsequent test
             * attempts.
             */
#ifndef WIN32
            if (kill(postmaster_pid, SIGKILL) != 0 &&
                errno != ESRCH)
                fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n"),
                        progname, strerror(errno));
#else
            if (TerminateProcess(postmaster_pid, 255) == 0)
                fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n"),
                        progname, GetLastError());
#endif

            exit(2);
        }

        postmaster_running = true;

#ifdef WIN64
/* need a series of two casts to convert HANDLE without compiler warning */
#define ULONGPID(x) (unsigned long) (unsigned long long) (x)
#else
#define ULONGPID(x) (unsigned long) (x)
#endif
        printf(_("running on port %d with PID %lu\n"),
               port, ULONGPID(postmaster_pid));
    }
    else
    {
        /*
         * Using an existing installation, so may need to get rid of
         * pre-existing database(s) and role(s)
         */
        if (!use_existing)
        {
            for (sl = dblist; sl; sl = sl->next)
                drop_database_if_exists(sl->str);
            for (sl = extraroles; sl; sl = sl->next)
                drop_role_if_exists(sl->str);
        }
    }

    /*
     * Create the test database(s) and role(s)
     */
    if (!use_existing)
    {
        for (sl = dblist; sl; sl = sl->next)
            create_database(sl->str);
        for (sl = extraroles; sl; sl = sl->next)
            create_role(sl->str, dblist);
    }

    /*
     * Ready to run the tests
     */
    header(_("running regression test queries"));

    for (sl = schedulelist; sl != NULL; sl = sl->next)
    {
        run_schedule(sl->str, tfunc);
    }

    for (sl = extra_tests; sl != NULL; sl = sl->next)
    {
        run_single_test(sl->str, tfunc);
    }

    /*
     * Shut down temp installation's postmaster
     */
    if (temp_install)
    {
        header(_("shutting down postmaster"));
        stop_postmaster();
    }

    fclose(logfile);

    /*
     * Emit nice-looking summary message
     */
    if (fail_count == 0 && fail_ignore_count == 0)
        snprintf(buf, sizeof(buf),
                 _(" All %d tests passed. "),
                 success_count);
    else if (fail_count == 0)   /* fail_count=0, fail_ignore_count>0 */
        snprintf(buf, sizeof(buf),
                 _(" %d of %d tests passed, %d failed test(s) ignored. "),
                 success_count,
                 success_count + fail_ignore_count,
                 fail_ignore_count);
    else if (fail_ignore_count == 0)    /* fail_count>0 && fail_ignore_count=0 */
        snprintf(buf, sizeof(buf),
                 _(" %d of %d tests failed. "),
                 fail_count,
                 success_count + fail_count);
    else
        /* fail_count>0 && fail_ignore_count>0 */
        snprintf(buf, sizeof(buf),
                 _(" %d of %d tests failed, %d of these failures ignored. "),
                 fail_count + fail_ignore_count,
                 success_count + fail_count + fail_ignore_count,
                 fail_ignore_count);

    putchar('\n');
    for (i = strlen(buf); i > 0; i--)
        putchar('=');
    printf("\n%s\n", buf);
    for (i = strlen(buf); i > 0; i--)
        putchar('=');
    putchar('\n');
    putchar('\n');

    if (file_size(difffilename) > 0)
    {
        printf(_("The differences that caused some tests to fail can be viewed in the\n"
                 "file \"%s\".  A copy of the test summary that you see\n"
                 "above is saved in the file \"%s\".\n\n"),
               difffilename, logfilename);
    }
    else
    {
        unlink(difffilename);
        unlink(logfilename);
    }

    if (fail_count != 0)
        exit(1);

    return 0;
}

void replace_string ( char *  string,
char *  replace,
char *  replacement 
)

Definition at line 388 of file pg_regress.c.

References free, NULL, and strlcpy().

Referenced by convert_sourcefiles_in(), ecpg_filter(), and ecpg_start_test().

{
    char       *ptr;

    while ((ptr = strstr(string, replace)) != NULL)
    {
        char       *dup = strdup(string);

        strlcpy(string, dup, ptr - string + 1);
        strcat(string, replacement);
        strcat(string, dup + (ptr - string) + strlen(replace));
        free(dup);
    }
}

static bool results_differ ( const char *  testname,
const char *  resultsfile,
const char *  default_expectfile 
) [static]

Definition at line 1218 of file pg_regress.c.

References basic_diff_opts, difffilename, file_exists(), file_line_count(), free, get_alternative_expectfile(), get_expectfile(), i, MAXPGPATH, pretty_diff_opts, run_diff(), snprintf(), SYSTEMQUOTE, and unlink().

Referenced by run_schedule(), and run_single_test().

{
    char        expectfile[MAXPGPATH];
    char        diff[MAXPGPATH];
    char        cmd[MAXPGPATH * 3];
    char        best_expect_file[MAXPGPATH];
    FILE       *difffile;
    int         best_line_count;
    int         i;
    int         l;
    const char *platform_expectfile;

    /*
     * We can pass either the resultsfile or the expectfile, they should have
     * the same type (filename.type) anyway.
     */
    platform_expectfile = get_expectfile(testname, resultsfile);

    strcpy(expectfile, default_expectfile);
    if (platform_expectfile)
    {
        /*
         * Replace everything afer the last slash in expectfile with what the
         * platform_expectfile contains.
         */
        char       *p = strrchr(expectfile, '/');

        if (p)
            strcpy(++p, platform_expectfile);
    }

    /* Name to use for temporary diff file */
    snprintf(diff, sizeof(diff), "%s.diff", resultsfile);

    /* OK, run the diff */
    snprintf(cmd, sizeof(cmd),
             SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
             basic_diff_opts, expectfile, resultsfile, diff);

    /* Is the diff file empty? */
    if (run_diff(cmd, diff) == 0)
    {
        unlink(diff);
        return false;
    }

    /* There may be secondary comparison files that match better */
    best_line_count = file_line_count(diff);
    strcpy(best_expect_file, expectfile);

    for (i = 0; i <= 9; i++)
    {
        char       *alt_expectfile;

        alt_expectfile = get_alternative_expectfile(expectfile, i);
        if (!file_exists(alt_expectfile))
            continue;

        snprintf(cmd, sizeof(cmd),
                 SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
                 basic_diff_opts, alt_expectfile, resultsfile, diff);

        if (run_diff(cmd, diff) == 0)
        {
            unlink(diff);
            return false;
        }

        l = file_line_count(diff);
        if (l < best_line_count)
        {
            /* This diff was a better match than the last one */
            best_line_count = l;
            strcpy(best_expect_file, alt_expectfile);
        }
        free(alt_expectfile);
    }

    /*
     * fall back on the canonical results file if we haven't tried it yet and
     * haven't found a complete match yet.
     */

    if (platform_expectfile)
    {
        snprintf(cmd, sizeof(cmd),
                 SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
                 basic_diff_opts, default_expectfile, resultsfile, diff);

        if (run_diff(cmd, diff) == 0)
        {
            /* No diff = no changes = good */
            unlink(diff);
            return false;
        }

        l = file_line_count(diff);
        if (l < best_line_count)
        {
            /* This diff was a better match than the last one */
            best_line_count = l;
            strcpy(best_expect_file, default_expectfile);
        }
    }

    /*
     * Use the best comparison file to generate the "pretty" diff, which we
     * append to the diffs summary file.
     */
    snprintf(cmd, sizeof(cmd),
             SYSTEMQUOTE "diff %s \"%s\" \"%s\" >> \"%s\"" SYSTEMQUOTE,
             pretty_diff_opts, best_expect_file, resultsfile, difffilename);
    run_diff(cmd, difffilename);

    /* And append a separator */
    difffile = fopen(difffilename, "a");
    if (difffile)
    {
        fprintf(difffile,
                "\n======================================================================\n\n");
        fclose(difffile);
    }

    unlink(diff);
    return true;
}

static int run_diff ( const char *  cmd,
const char *  filename 
) [static]

Definition at line 1185 of file pg_regress.c.

References _, file_size(), system(), WEXITSTATUS, and WIFEXITED.

Referenced by results_differ().

{
    int         r;

    r = system(cmd);
    if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
    {
        fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
        exit(2);
    }
#ifdef WIN32

    /*
     * On WIN32, if the 'diff' command cannot be found, system() returns 1,
     * but produces nothing to stdout, so we check for that here.
     */
    if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
    {
        fprintf(stderr, _("diff command not found: %s\n"), cmd);
        exit(2);
    }
#endif

    return WEXITSTATUS(r);
}

static void run_schedule ( const char *  schedule,
test_function  tfunc 
) [static]

Definition at line 1453 of file pg_regress.c.

References _, add_stringlist_item(), fail_count, fail_ignore_count, free_stringlist(), i, log_child_failure(), max_connections, MAX_PARALLEL_TESTS, _stringlist::next, NULL, PID_TYPE, progname, results_differ(), status(), status_end(), _stringlist::str, strerror(), success_count, test(), and wait_for_tests().

Referenced by regression_main().

{
#define MAX_PARALLEL_TESTS 100
    char       *tests[MAX_PARALLEL_TESTS];
    _stringlist *resultfiles[MAX_PARALLEL_TESTS];
    _stringlist *expectfiles[MAX_PARALLEL_TESTS];
    _stringlist *tags[MAX_PARALLEL_TESTS];
    PID_TYPE    pids[MAX_PARALLEL_TESTS];
    int         statuses[MAX_PARALLEL_TESTS];
    _stringlist *ignorelist = NULL;
    char        scbuf[1024];
    FILE       *scf;
    int         line_num = 0;

    memset(resultfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
    memset(expectfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
    memset(tags, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);

    scf = fopen(schedule, "r");
    if (!scf)
    {
        fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
                progname, schedule, strerror(errno));
        exit(2);
    }

    while (fgets(scbuf, sizeof(scbuf), scf))
    {
        char       *test = NULL;
        char       *c;
        int         num_tests;
        bool        inword;
        int         i;

        line_num++;

        for (i = 0; i < MAX_PARALLEL_TESTS; i++)
        {
            if (resultfiles[i] == NULL)
                break;
            free_stringlist(&resultfiles[i]);
            free_stringlist(&expectfiles[i]);
            free_stringlist(&tags[i]);
        }

        /* strip trailing whitespace, especially the newline */
        i = strlen(scbuf);
        while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
            scbuf[--i] = '\0';

        if (scbuf[0] == '\0' || scbuf[0] == '#')
            continue;
        if (strncmp(scbuf, "test: ", 6) == 0)
            test = scbuf + 6;
        else if (strncmp(scbuf, "ignore: ", 8) == 0)
        {
            c = scbuf + 8;
            while (*c && isspace((unsigned char) *c))
                c++;
            add_stringlist_item(&ignorelist, c);

            /*
             * Note: ignore: lines do not run the test, they just say that
             * failure of this test when run later on is to be ignored. A bit
             * odd but that's how the shell-script version did it.
             */
            continue;
        }
        else
        {
            fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
                    schedule, line_num, scbuf);
            exit(2);
        }

        num_tests = 0;
        inword = false;
        for (c = test; *c; c++)
        {
            if (isspace((unsigned char) *c))
            {
                *c = '\0';
                inword = false;
            }
            else if (!inword)
            {
                if (num_tests >= MAX_PARALLEL_TESTS)
                {
                    /* can't print scbuf here, it's already been trashed */
                    fprintf(stderr, _("too many parallel tests in schedule file \"%s\", line %d\n"),
                            schedule, line_num);
                    exit(2);
                }
                tests[num_tests] = c;
                num_tests++;
                inword = true;
            }
        }

        if (num_tests == 0)
        {
            fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
                    schedule, line_num, scbuf);
            exit(2);
        }

        if (num_tests == 1)
        {
            status(_("test %-24s ... "), tests[0]);
            pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
            wait_for_tests(pids, statuses, NULL, 1);
            /* status line is finished below */
        }
        else if (max_connections > 0 && max_connections < num_tests)
        {
            int         oldest = 0;

            status(_("parallel group (%d tests, in groups of %d): "),
                   num_tests, max_connections);
            for (i = 0; i < num_tests; i++)
            {
                if (i - oldest >= max_connections)
                {
                    wait_for_tests(pids + oldest, statuses + oldest,
                                   tests + oldest, i - oldest);
                    oldest = i;
                }
                pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
            }
            wait_for_tests(pids + oldest, statuses + oldest,
                           tests + oldest, i - oldest);
            status_end();
        }
        else
        {
            status(_("parallel group (%d tests): "), num_tests);
            for (i = 0; i < num_tests; i++)
            {
                pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
            }
            wait_for_tests(pids, statuses, tests, num_tests);
            status_end();
        }

        /* Check results for all tests */
        for (i = 0; i < num_tests; i++)
        {
            _stringlist *rl,
                       *el,
                       *tl;
            bool        differ = false;

            if (num_tests > 1)
                status(_("     %-24s ... "), tests[i]);

            /*
             * Advance over all three lists simultaneously.
             *
             * Compare resultfiles[j] with expectfiles[j] always. Tags are
             * optional but if there are tags, the tag list has the same
             * length as the other two lists.
             */
            for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
                 rl != NULL;    /* rl and el have the same length */
                 rl = rl->next, el = el->next)
            {
                bool        newdiff;

                if (tl)
                    tl = tl->next;      /* tl has the same length as rl and el
                                         * if it exists */

                newdiff = results_differ(tests[i], rl->str, el->str);
                if (newdiff && tl)
                {
                    printf("%s ", tl->str);
                }
                differ |= newdiff;
            }

            if (differ)
            {
                bool        ignore = false;
                _stringlist *sl;

                for (sl = ignorelist; sl != NULL; sl = sl->next)
                {
                    if (strcmp(tests[i], sl->str) == 0)
                    {
                        ignore = true;
                        break;
                    }
                }
                if (ignore)
                {
                    status(_("failed (ignored)"));
                    fail_ignore_count++;
                }
                else
                {
                    status(_("FAILED"));
                    fail_count++;
                }
            }
            else
            {
                status(_("ok"));
                success_count++;
            }

            if (statuses[i] != 0)
                log_child_failure(statuses[i]);

            status_end();
        }
    }

    fclose(scf);
}

static void run_single_test ( const char *  test,
test_function  tfunc 
) [static]

Definition at line 1677 of file pg_regress.c.

References _, fail_count, log_child_failure(), _stringlist::next, NULL, PID_TYPE, results_differ(), status(), status_end(), _stringlist::str, success_count, and wait_for_tests().

Referenced by regression_main().

{
    PID_TYPE    pid;
    int         exit_status;
    _stringlist *resultfiles = NULL;
    _stringlist *expectfiles = NULL;
    _stringlist *tags = NULL;
    _stringlist *rl,
               *el,
               *tl;
    bool        differ = false;

    status(_("test %-24s ... "), test);
    pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
    wait_for_tests(&pid, &exit_status, NULL, 1);

    /*
     * Advance over all three lists simultaneously.
     *
     * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
     * but if there are tags, the tag list has the same length as the other
     * two lists.
     */
    for (rl = resultfiles, el = expectfiles, tl = tags;
         rl != NULL;            /* rl and el have the same length */
         rl = rl->next, el = el->next)
    {
        bool        newdiff;

        if (tl)
            tl = tl->next;      /* tl has the same length as rl and el if it
                                 * exists */

        newdiff = results_differ(test, rl->str, el->str);
        if (newdiff && tl)
        {
            printf("%s ", tl->str);
        }
        differ |= newdiff;
    }

    if (differ)
    {
        status(_("FAILED"));
        fail_count++;
    }
    else
    {
        status(_("ok"));
        success_count++;
    }

    if (exit_status != 0)
        log_child_failure(exit_status);

    status_end();
}

PID_TYPE spawn_process ( const char *  cmdline  ) 

Definition at line 934 of file pg_regress.c.

References _, BOOL(), free, logfile, malloc, NULL, progname, shellprog, strerror(), and TRUE.

Referenced by ecpg_start_test(), isolation_start_test(), psql_start_test(), and regression_main().

{
#ifndef WIN32
    pid_t       pid;

    /*
     * Must flush I/O buffers before fork.  Ideally we'd use fflush(NULL) here
     * ... does anyone still care about systems where that doesn't work?
     */
    fflush(stdout);
    fflush(stderr);
    if (logfile)
        fflush(logfile);

    pid = fork();
    if (pid == -1)
    {
        fprintf(stderr, _("%s: could not fork: %s\n"),
                progname, strerror(errno));
        exit(2);
    }
    if (pid == 0)
    {
        /*
         * In child
         *
         * Instead of using system(), exec the shell directly, and tell it to
         * "exec" the command too.  This saves two useless processes per
         * parallel test case.
         */
        char       *cmdline2 = malloc(strlen(cmdline) + 6);

        sprintf(cmdline2, "exec %s", cmdline);
        execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
        fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
                progname, shellprog, strerror(errno));
        _exit(1);               /* not exit() here... */
    }
    /* in parent */
    return pid;
#else
    char       *cmdline2;
    BOOL        b;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    HANDLE      origToken;
    HANDLE      restrictedToken;
    SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
    SID_AND_ATTRIBUTES dropSids[2];
    __CreateRestrictedToken _CreateRestrictedToken = NULL;
    HANDLE      Advapi32Handle;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);

    Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
    if (Advapi32Handle != NULL)
    {
        _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
    }

    if (_CreateRestrictedToken == NULL)
    {
        if (Advapi32Handle != NULL)
            FreeLibrary(Advapi32Handle);
        fprintf(stderr, _("%s: cannot create restricted tokens on this platform\n"),
                progname);
        exit(2);
    }

    /* Open the current token to use as base for the restricted one */
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
    {
        fprintf(stderr, _("could not open process token: error code %lu\n"),
                GetLastError());
        exit(2);
    }

    /* Allocate list of SIDs to remove */
    ZeroMemory(&dropSids, sizeof(dropSids));
    if (!AllocateAndInitializeSid(&NtAuthority, 2,
                                  SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &dropSids[0].Sid) ||
        !AllocateAndInitializeSid(&NtAuthority, 2,
                                  SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &dropSids[1].Sid))
    {
        fprintf(stderr, _("could not allocate SIDs: error code %lu\n"), GetLastError());
        exit(2);
    }

    b = _CreateRestrictedToken(origToken,
                               DISABLE_MAX_PRIVILEGE,
                               sizeof(dropSids) / sizeof(dropSids[0]),
                               dropSids,
                               0, NULL,
                               0, NULL,
                               &restrictedToken);

    FreeSid(dropSids[1].Sid);
    FreeSid(dropSids[0].Sid);
    CloseHandle(origToken);
    FreeLibrary(Advapi32Handle);

    if (!b)
    {
        fprintf(stderr, _("could not create restricted token: error code %lu\n"),
                GetLastError());
        exit(2);
    }

    cmdline2 = malloc(strlen(cmdline) + 8);
    sprintf(cmdline2, "cmd /c %s", cmdline);

#ifndef __CYGWIN__
    AddUserToTokenDacl(restrictedToken);
#endif

    if (!CreateProcessAsUser(restrictedToken,
                             NULL,
                             cmdline2,
                             NULL,
                             NULL,
                             TRUE,
                             CREATE_SUSPENDED,
                             NULL,
                             NULL,
                             &si,
                             &pi))
    {
        fprintf(stderr, _("could not start process for \"%s\": error code %lu\n"),
                cmdline2, GetLastError());
        exit(2);
    }

    free(cmdline2);

    ResumeThread(pi.hThread);
    CloseHandle(pi.hThread);
    return pi.hProcess;
#endif
}

static void split_to_stringlist ( const char *  s,
const char *  delim,
_stringlist **  listhead 
) [static]

Definition at line 216 of file pg_regress.c.

References add_stringlist_item(), free, and NULL.

Referenced by regression_main().

{
    char       *sc = strdup(s);
    char       *token = strtok(sc, delim);

    while (token)
    {
        add_stringlist_item(listhead, token);
        token = strtok(NULL, delim);
    }
    free(sc);
}

static void status ( const char *  fmt,
  ... 
) [static]
static void status_end ( void   )  [static]

Definition at line 271 of file pg_regress.c.

References logfile.

Referenced by run_schedule(), and run_single_test().

{
    fprintf(stdout, "\n");
    fflush(stdout);
    if (logfile)
        fprintf(logfile, "\n");
}

static void stop_postmaster ( void   )  [static]

Definition at line 283 of file pg_regress.c.

References _, bindir, buf, MAXPGPATH, postmaster_running, progname, snprintf(), system(), SYSTEMQUOTE, and temp_install.

{
    if (postmaster_running)
    {
        /* We use pg_ctl to issue the kill and wait for stop */
        char        buf[MAXPGPATH * 2];
        int         r;

        /* On Windows, system() seems not to force fflush, so... */
        fflush(stdout);
        fflush(stderr);

        snprintf(buf, sizeof(buf),
                 SYSTEMQUOTE "\"%s/pg_ctl\" stop -D \"%s/data\" -s -m fast" SYSTEMQUOTE,
                 bindir, temp_install);
        r = system(buf);
        if (r != 0)
        {
            fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
                    progname, r);
            _exit(2);           /* not exit(), that could be recursive */
        }

        postmaster_running = false;
    }
}

static bool string_matches_pattern ( const char *  str,
const char *  pattern 
) [static]

Definition at line 323 of file pg_regress.c.

Referenced by load_resultmap().

{
    while (*str && *pattern)
    {
        if (*pattern == '.' && pattern[1] == '*')
        {
            pattern += 2;
            /* Trailing .* matches everything. */
            if (*pattern == '\0')
                return true;

            /*
             * Otherwise, scan for a text position at which we can match the
             * rest of the pattern.
             */
            while (*str)
            {
                /*
                 * Optimization to prevent most recursion: don't recurse
                 * unless first pattern char might match this text char.
                 */
                if (*str == *pattern || *pattern == '.')
                {
                    if (string_matches_pattern(str, pattern))
                        return true;
                }

                str++;
            }

            /*
             * End of text with no match.
             */
            return false;
        }
        else if (*pattern != '.' && *str != *pattern)
        {
            /*
             * Not the single-character wildcard and no explicit match? Then
             * time to quit...
             */
            return false;
        }

        str++;
        pattern++;
    }

    if (*pattern == '\0')
        return true;            /* end of pattern, so declare match */

    /* End of input string.  Do we have matching pattern remaining? */
    while (*pattern == '.' && pattern[1] == '*')
        pattern += 2;
    if (*pattern == '\0')
        return true;            /* end of pattern, so declare match */

    return false;
}

static void wait_for_tests ( PID_TYPE *  pids,
int *  statuses,
char **  names,
int  num_tests 
) [static]

Definition at line 1354 of file pg_regress.c.

References _, FALSE, free, i, INVALID_PID, malloc, PID_TYPE, status(), and strerror().

Referenced by run_schedule(), and run_single_test().

{
    int         tests_left;
    int         i;

#ifdef WIN32
    PID_TYPE   *active_pids = malloc(num_tests * sizeof(PID_TYPE));

    memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
#endif

    tests_left = num_tests;
    while (tests_left > 0)
    {
        PID_TYPE    p;

#ifndef WIN32
        int         exit_status;

        p = wait(&exit_status);

        if (p == INVALID_PID)
        {
            fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
                    strerror(errno));
            exit(2);
        }
#else
        DWORD       exit_status;
        int         r;

        r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
        if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
        {
            fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"),
                    GetLastError());
            exit(2);
        }
        p = active_pids[r - WAIT_OBJECT_0];
        /* compact the active_pids array */
        active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
#endif   /* WIN32 */

        for (i = 0; i < num_tests; i++)
        {
            if (p == pids[i])
            {
#ifdef WIN32
                GetExitCodeProcess(pids[i], &exit_status);
                CloseHandle(pids[i]);
#endif
                pids[i] = INVALID_PID;
                statuses[i] = (int) exit_status;
                if (names)
                    status(" %s", names[i]);
                tests_left--;
                break;
            }
        }
    }

#ifdef WIN32
    free(active_pids);
#endif
}


Variable Documentation

const char* basic_diff_opts = ""

Definition at line 74 of file pg_regress.c.

Referenced by results_differ().

char* bindir = PGBINDIR

Definition at line 55 of file pg_regress.c.

Referenced by initialize_environment(), regression_main(), and stop_postmaster().

char* datadir = PGSHAREDIR

Definition at line 57 of file pg_regress.c.

Referenced by fuzzy_open_file(), initialize_environment(), and regression_main().

bool debug = false

Definition at line 83 of file pg_regress.c.

char* difffilename [static]

Definition at line 111 of file pg_regress.c.

Referenced by open_result_files(), regression_main(), and results_differ().

char* dlpath = PKGLIBDIR [static]

Definition at line 102 of file pg_regress.c.

Referenced by convert_sourcefiles_in(), and regression_main().

char* encoding = NULL [static]

Definition at line 91 of file pg_regress.c.

Referenced by create_database(), initialize_environment(), and regression_main().

_stringlist* extra_install = NULL [static]

Definition at line 105 of file pg_regress.c.

_stringlist* extra_tests = NULL [static]

Definition at line 93 of file pg_regress.c.

_stringlist* extraroles = NULL [static]

Definition at line 104 of file pg_regress.c.

int fail_count = 0 [static]

Definition at line 119 of file pg_regress.c.

Referenced by regression_main(), run_schedule(), and run_single_test().

int fail_ignore_count = 0 [static]

Definition at line 120 of file pg_regress.c.

Referenced by regression_main(), and run_schedule().

char* host_platform = HOST_TUPLE

Definition at line 58 of file pg_regress.c.

Referenced by load_resultmap().

char* hostname = NULL [static]

Definition at line 99 of file pg_regress.c.

Referenced by initialize_environment(), and regression_main().

char* inputdir = "."
char* launcher = NULL

Definition at line 87 of file pg_regress.c.

Referenced by isolation_start_test(), psql_start_test(), and regression_main().

char* libdir = LIBDIR

Definition at line 56 of file pg_regress.c.

Referenced by initialize_environment().

_stringlist* loadextension = NULL [static]

Definition at line 89 of file pg_regress.c.

_stringlist* loadlanguage = NULL [static]

Definition at line 88 of file pg_regress.c.

FILE* logfile [static]
char* logfilename [static]

Definition at line 109 of file pg_regress.c.

Referenced by open_result_files(), and regression_main().

char* makeprog = MAKEPROG [static]

Definition at line 61 of file pg_regress.c.

Referenced by regression_main().

int max_connections = 0 [static]

Definition at line 90 of file pg_regress.c.

Referenced by regression_main(), and run_schedule().

bool nolocale = false [static]

Definition at line 97 of file pg_regress.c.

Referenced by create_database(), initialize_environment(), and regression_main().

char* outputdir = "."
int port = -1 [static]
bool port_specified_by_user = false [static]

Definition at line 101 of file pg_regress.c.

Referenced by regression_main().

PID_TYPE postmaster_pid = INVALID_PID [static]

Definition at line 115 of file pg_regress.c.

Referenced by regression_main().

bool postmaster_running = false [static]

Definition at line 116 of file pg_regress.c.

Referenced by regression_main(), and stop_postmaster().

const char* pretty_diff_opts = "-C3"

Definition at line 75 of file pg_regress.c.

Referenced by regression_main(), and results_differ().

const char* progname [static]

Definition at line 108 of file pg_regress.c.

char* psqldir = PGBINDIR
_resultmap* resultmap = NULL [static]

Definition at line 113 of file pg_regress.c.

_stringlist* schedulelist = NULL [static]

Definition at line 92 of file pg_regress.c.

char* shellprog = SHELLPROG [static]

Definition at line 65 of file pg_regress.c.

Referenced by spawn_process().

int success_count = 0 [static]

Definition at line 118 of file pg_regress.c.

Referenced by regression_main(), run_schedule(), and run_single_test().

char* temp_config = NULL [static]

Definition at line 95 of file pg_regress.c.

Referenced by regression_main().

char* temp_install = NULL [static]

Definition at line 94 of file pg_regress.c.

Referenced by initialize_environment(), regression_main(), and stop_postmaster().

char* top_builddir = NULL [static]

Definition at line 96 of file pg_regress.c.

Referenced by regression_main().

bool use_existing = false [static]

Definition at line 98 of file pg_regress.c.

Referenced by regression_main().

char* user = NULL [static]