diff --git a/src/bin/initdb/Makefile b/src/bin/initdb/Makefile index cd2cb86c36..995a379355 100644 --- a/src/bin/initdb/Makefile +++ b/src/bin/initdb/Makefile @@ -5,7 +5,7 @@ # Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Makefile,v 1.29 2003/09/07 03:36:03 tgl Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Makefile,v 1.30 2003/11/10 22:51:16 momjian Exp $ # #------------------------------------------------------------------------- @@ -13,25 +13,30 @@ subdir = src/bin/initdb top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -all: initdb +override CPPFLAGS := -DPGBINDIR=\"$(bindir)\" -DPGDATADIR=\"$(datadir)\" -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS) -initdb: initdb.sh $(top_builddir)/src/Makefile.global - sed -e 's/@VERSION@/$(VERSION)/g' \ - -e 's,@SHELL@,$(SHELL),g' \ - -e 's,@HAVE_IPV6@,$(HAVE_IPV6),g' \ - -e 's,@bindir@,$(bindir),g' \ - -e 's,@datadir@,$(datadir),g' \ - $< >$@ - chmod a+x $@ +OBJS= initdb.o sprompt.o +ifeq ($(PORTNAME), win32) +OBJS+=dirmod.o +endif + +all: submake-libpq submake-libpgport initdb + +initdb: $(OBJS) $(libpq_builddir)/libpq.a + $(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@ + +dirmod.c sprompt.c: % : $(top_srcdir)/src/port/% + rm -f $@ && $(LN_S) $< . install: all installdirs - $(INSTALL_SCRIPT) initdb $(DESTDIR)$(bindir)/initdb + $(INSTALL_PROGRAM) initdb$(X) $(DESTDIR)$(bindir)/initdb$(X) + $(INSTALL_DATA) $(srcdir)/system_views.sql $(DESTDIR)$(datadir)/system_views.sql installdirs: $(mkinstalldirs) $(DESTDIR)$(bindir) uninstall: - rm -f $(DESTDIR)$(bindir)/initdb + rm -f $(DESTDIR)$(bindir)/initdb$(X) $(DESTDIR)$(datadir)/system_views.sql -clean distclean maintainer-clean: - rm -f initdb +clean distclean: + rm -f initdb$(X) $(OBJS) diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c new file mode 100644 index 0000000000..13522be5a3 --- /dev/null +++ b/src/bin/initdb/initdb.c @@ -0,0 +1,2493 @@ + +/*------------------------------------------------------------------------- + * + * initdb + * + * author: Andrew Dunstan mailto:andrew@dunslane.net + * + * Copyright (C) 2003 Andrew Dunstan + * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * This code is released under the terms of the PostgreSQL License. + * + * This is a C implementation of the previous shell script for setting up a + * PostgreSQL cluster location, and should be highly compatible with it. + * + * $Header: /cvsroot/pgsql/src/bin/initdb/initdb.c,v 1.1 2003/11/10 22:51:16 momjian Exp $ + * + * TODO: + * - clean up find_postgres code and return values + * + * Note: + * The program has some memory leakage - it isn't worth cleaning it up. + * Even before the code was put in to free most of the dynamic memory + * used it ran around 500Kb used + malloc overhead. It should now use + * far less than that (around 240Kb - the size of the BKI file). + * If we can't load this much data into memory how will we ever run + * postgres anyway? + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include "getopt_long.h" +#include +#include +#include +#include + +#include + +#include "libpq/pqsignal.h" +#include "mb/pg_wchar.h" + + +/* version string we expect back from postgres */ +#define PG_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n" + +/* values passed in by makefile define */ + +char *bindir = PGBINDIR; +char *datadir = PGDATADIR; + +/* values to be obtained from arguments */ + +char *pg_data = ""; +char *encoding = ""; +char *locale = ""; +char *lc_collate = ""; +char *lc_ctype = ""; +char *lc_monetary = ""; +char *lc_numeric = ""; +char *lc_time = ""; +char *lc_messages = ""; +char *username = ""; +bool pwprompt = false; +bool debug = false; +bool noclean = false; +bool show_help = false; +bool show_version = false; +bool show_setting = false; + + +/* internal vars */ +char *progname; +char *self_path; +char *postgres; +char *encodingid = "0"; +char *bki_file; +char *desc_file; +char *hba_file; +char *ident_file; +char *conf_file; +char *conversion_file; +char *info_schema_file; +char *features_file; +char *system_views_file; +char *effective_user; +bool testpath = true; +bool made_new_pgdata = false; +char infoversion[100]; +bool not_ok = false; + +/* defaults */ +int n_buffers = 50; +int n_connections = 10; + + +/* platform specific path stuff */ +#if defined(__CYGWIN__) || defined(WIN32) +#define EXE ".exe" +#define DEVNULL "nul" +#else +#define EXE "" +#define DEVNULL "/dev/null" +#endif /* defined(__CYGWIN__) || defined(WIN32) */ + +#ifdef WIN32 +#define PATHSEP ';' +#else +#define PATHSEP ':' +#endif /* WIN32 */ + +/* detected path to postgres and (we assume) friends */ +char *pgpath; + +/* forward declare all our functions */ +static bool rmtree(char *, bool); +static void exit_nicely(void); +static void canonicalise_path(char *); + +#ifdef WIN32 +static char *expanded_path(char *); +static int init_unlink(const char *); + +#else +#define expanded_path(x) ( x ) +#define init_unlink(x) unlink( (x) ) +#endif /* WIN32 */ + +static char **readfile(char *); +static void writefile(char *, char **); +static char *get_id(void); +static char *get_encoding_id(char *); +static char *get_short_version(void); +static int build_path(char *, mode_t); +static bool check_data_dir(void); +static bool mkdatadir(char *); +static bool chklocale(const char *); +static void setlocales(void); +static void set_input(char **, char *); +static void check_input(char *path); +static int find_postgres(char *); +static int set_paths(void); +static char **replace_token(char **, char *, char *); +static void set_short_version(char *, char *); +static void set_null_conf(void); +static void test_buffers(void); +static void test_connections(void); +static void setup_config(void); +static void bootstrap_template1(char *); +static void setup_shadow(void); +static void get_set_pw(void); +static void unlimit_systables(void); +static void setup_depend(void); +static void setup_sysviews(void); +static void setup_description(void); +static void setup_conversion(void); +static void setup_privileges(void); +static void set_info_version(void); +static void setup_schema(void); +static void vacuum_db(void); +static void make_template0(void); +static void usage(void); +static void trapsig(int); +static void check_ok(void); +static char *xstrdup(const char *); +static void *xmalloc(size_t); + +/* + * macros for running pipes to postgres + */ + +#define PG_CMD_DECL char cmd[MAXPGPATH]; char ** line ; FILE * pg +#define PG_CMD_DECL_NOLINE char cmd[MAXPGPATH]; FILE * pg +#define PG_CMD_OPEN \ + do {\ + pg = popen(cmd,PG_BINARY_W);\ + if (pg == NULL) \ + exit_nicely();\ + } while (0) +#define PG_CMD_CLOSE \ + do {\ + if(pclose(pg)>>8 &0xff)\ + exit_nicely();\ + } while (0) +#define PG_CMD_PUTLINE \ + do {\ + if (fputs(*line, pg) < 0) \ + exit_nicely(); \ + fflush(pg);\ + } while (0) + + + + +#ifdef WIN32 + +/* workaround for win32 unlink bug, not using logging like in port/dirmod.c */ + +/* make sure we call the real unlink from MSVCRT */ + +#ifdef unlink +#undef unlink +#endif + +static int +init_unlink(const char *path) +{ + while (unlink(path)) + { + if (errno != EACCES) + return -1; + Sleep(100); /* ms */ + } + return 0; +} +#endif /* WIN32 */ + +/* + * routines to check mem allocations and fail noisily + * Note that we can't call exit_nicely() on a memory failure, as it calls + * rmtree() which needs memory allocation. So we just exit with a bang. + * + */ + +static void * +xmalloc(size_t size) +{ + void *result; + + result = malloc(size); + if (!result) + { + fputs("malloc failure - bailing out\n", stderr); + exit(1); + } + return result; +} + +static char * +xstrdup(const char *s) +{ + char *result; + + result = strdup(s); + if (!result) + { + fputs("strdup failure - bailing out\n", stderr); + exit(1); + } + return result; +} + +/* + * delete a directory tree recursively + * assumes path points to a valid directory + * deletes everything under path + * if rmtopdir is true deletes the directory too + * + */ +static bool +rmtree(char *path, bool rmtopdir) +{ + char filepath[MAXPGPATH]; + DIR *dir; + struct dirent *file; + char **filenames; + char **filename; + int numnames = 0; + struct stat statbuf; + + /* + * we copy all the names out of the directory before we start + * modifying it. + * + */ + + dir = opendir(path); + if (dir == NULL) + return false; + + while ((file = readdir(dir)) != NULL) + { + if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0) + numnames++; + } + + rewinddir(dir); + + filenames = xmalloc((numnames + 2) * sizeof(char *)); + numnames = 0; + + while ((file = readdir(dir)) != NULL) + { + if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0) + filenames[numnames++] = xstrdup(file->d_name); + } + + filenames[numnames] = NULL; + + closedir(dir); + + /* now we have the names we can start removing things */ + + for (filename = filenames; *filename; filename++) + { + snprintf(filepath, MAXPGPATH, "%s/%s", path, *filename); + + if (stat(filepath, &statbuf) != 0) + return false; + + if (S_ISDIR(statbuf.st_mode)) + { + /* call ourselves recursively for a directory */ + if (!rmtree(filepath, true)) + return false; + } + else + { + if (init_unlink(filepath) != 0) + return false; + } + } + + if (rmtopdir) + { + if (rmdir(path) != 0) + return false; + } + + return true; +} + + +/* + * make all paths look like unix, with forward slashes + * also strip any trailing slash + */ + +static void +canonicalise_path(char *path) +{ + char *p; + + for (p = path; *p; p++) + { +#ifdef WIN32 + if (*p == '\\') + *p = '/'; +#endif /* WIN32 */ + } + if (p != path && *--p == '/') + *p = '\0'; +} + +/* + * make a copy of the array of lines, with token replaced by replacement + * the first time it occurs on each line. + * This does most of what sed was used for in the shell script, but + * doesn't need any regexp stuff. + */ + +static char ** +replace_token(char **lines, char *token, char *replacement) +{ + int numlines = 1; + int i; + char **result; + int toklen, + replen, + diff; + + + for (i = 0; lines[i]; i++) + numlines++; + + result = (char **) xmalloc(numlines * sizeof(char *)); + + toklen = strlen(token); + replen = strlen(replacement); + diff = replen - toklen; + + for (i = 0; i < numlines; i++) + { + char *where; + char *newline; + int pre; + + /* just copy pointer if NULL or no change needed */ + + if (lines[i] == NULL || (where = strstr(lines[i], token)) == NULL) + { + result[i] = lines[i]; + continue; + } + + /* if we get here a change is needed - set up new line */ + + newline = (char *) xmalloc(strlen(lines[i]) + diff + 1); + + pre = where - lines[i]; + + strncpy(newline, lines[i], pre); + + strcpy(newline + pre, replacement); + + strcpy(newline + pre + replen, lines[i] + pre + toklen); + + result[i] = newline; + + } + + return result; + +} + +/* + * get the lines from a text file + * + */ +static char ** +readfile(char *path) +{ + FILE *infile; + int maxlength = 0, + linelen = 0; + int nlines = 0; + char **result; + char *buffer; + int c; + + infile = fopen(path, "r"); + + if (!infile) + { + fprintf(stderr, "could not read %s ... ", path); + exit_nicely(); + } + + /* pass over the file twice - the first time to size the result */ + + while ((c = fgetc(infile)) != EOF) + { + linelen++; + if (c == '\n') + { + nlines++; + if (linelen > maxlength) + maxlength = linelen; + linelen = 0; + } + } + + /* handle last line without a terminating newline (yuck) */ + + if (linelen) + nlines++; + if (linelen > maxlength) + maxlength = linelen; + + /* set up the result and the line buffer */ + + result = (char **) xmalloc((nlines + 2) * sizeof(char *)); + buffer = (char *) xmalloc(maxlength + 2); + + /* now reprocess the file and store the lines */ + + rewind(infile); + nlines = 0; + while (fgets(buffer, maxlength + 1, infile) != NULL) + { + result[nlines] = xstrdup(buffer); + nlines++; + } + + fclose(infile); + result[nlines] = NULL; + + return result; +} + +/* + * write an array of lines to a file + * + */ +static void +writefile(char *path, char **lines) +{ + FILE *out_file; + char **line; + + out_file = fopen(path, PG_BINARY_W); + if (out_file == NULL) + { + fprintf(stderr, "could not write %s ... ", path); + exit_nicely(); + } + for (line = lines; *line != NULL; line++) + { + if (fputs(*line, out_file) < 0) + exit_nicely(); + free(*line); + } + if (fclose(out_file)) + exit_nicely(); +} + +/* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */ + +/* + * this tries to build all the elements of a path to a directory a la mkdir -p + * we assume the path is in canonical form, i.e. uses / as the separator + * we also assume it isn't null. + * + */ + +static int +build_path(char *path, mode_t omode) +{ + struct stat sb; + mode_t numask, + oumask; + int first, + last, + retval; + char *p; + + p = path; + oumask = 0; + retval = 0; + +#ifdef WIN32 + + /* skip network and drive specifiers for win32 */ + if (strlen(p) >= 2) + { + if (p[0] == '/' && p[1] == '/') + { + /* network drive */ + p = strstr(p + 2, "/"); + if (p == NULL) + return 1; + } + else if (p[1] == ':' && + ((p[0] >= 'a' && p[0] <= 'z') || + (p[0] >= 'A' && p[0] <= 'Z'))) + { + /* local drive */ + p += 2; + } + } +#endif /* WIN32 */ + + if (p[0] == '/') /* Skip leading '/'. */ + ++p; + for (first = 1, last = 0; !last; ++p) + { + if (p[0] == '\0') + last = 1; + else if (p[0] != '/') + continue; + *p = '\0'; + if (p[1] == '\0') + last = 1; + if (first) + { + /* + * POSIX 1003.2: For each dir operand that does not name an + * existing directory, effects equivalent to those cased by + * the following command shall occcur: + * + * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] + * dir + * + * We change the user's umask and then restore it, instead of + * doing chmod's. + */ + oumask = umask(0); + numask = oumask & ~(S_IWUSR | S_IXUSR); + (void) umask(numask); + first = 0; + } + if (last) + (void) umask(oumask); + if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) + { + if (errno == EEXIST || errno == EISDIR) + { + if (stat(path, &sb) < 0) + { + retval = 1; + break; + } + else if (!S_ISDIR(sb.st_mode)) + { + if (last) + errno = EEXIST; + else + errno = ENOTDIR; + retval = 1; + break; + } + } + else + { + retval = 1; + break; + } + } + if (!last) + *p = '/'; + } + if (!first && !last) + (void) umask(oumask); + return (retval); +} + +/* + * clean up any files we created on failure + * if we created the data directory remove it too + * + */ +static void +exit_nicely(void) +{ + fprintf(stderr, "%s: failed\n", progname); + + if (!noclean) + { + if (made_new_pgdata) + { + fprintf(stderr, "%s: removing data directory \"%s\"\n", + progname, pg_data); + if (!rmtree(pg_data, true)) + fprintf(stderr, "%s: failed\n", progname); + } + else + { + fprintf(stderr, + "%s: removing contents of data directory \"%s\"\n", + progname, pg_data); + if (!rmtree(pg_data, false)) + fprintf(stderr, "%s: failed\n", progname); + } + } + exit(1); +} + +/* + * find the current user using code lifted from pg_id.c + * on unix make sure it isn't really root + * + */ +static char * +get_id(void) +{ +#ifndef WIN32 + + struct passwd *pw; + + pw = getpwuid(getuid()); + +#ifndef __BEOS__ /* no root check on BEOS */ + + if (!geteuid()) /* 0 is root's uid */ + { + fprintf(stderr, + "%s: cannot be run as root\n" + "Please log in (using, e.g., \"su\") as the (unprivileged) " + "user that will\n" "own the server process.\n", + progname); + exit(1); + } +#endif /* __BEOS__ */ + +#else /* the windows code */ + + struct passwd_win32 + { + int pw_uid; + char pw_name[128]; + } pass_win32; + struct passwd_win32 *pw = &pass_win32; + DWORD pwname_size = sizeof(pass_win32.pw_name) - 1; + + pw->pw_uid = 1; + GetUserName(pw->pw_name, &pwname_size); +#endif /* ! WIN32 */ + + return xstrdup(pw->pw_name); +} + +/* + * get the encoding id for a given encoding name + * + */ +static char * +get_encoding_id(char *encoding_name) +{ + int enc; + char result[20]; + + if (encoding_name && *encoding_name) + { + if ((enc = pg_char_to_encoding(encoding_name)) >= 0 && + pg_valid_server_encoding(encoding_name) >= 0) + { + sprintf(result, "%d", enc); + return xstrdup(result); + } + } + fprintf(stderr, "%s: \"%s\" is not a valid server encoding name\n", + progname, encoding_name ? encoding_name : "(null)"); + exit(1); +} + +/* + * get short version of VERSION + * + */ +static char * +get_short_version(void) +{ + bool gotdot = false; + int end; + char *vr; + + vr = xstrdup(PG_VERSION); + + for (end = 0; vr[end] != '\0'; end++) + { + if (vr[end] == '.') + { + if (end == 0) + return NULL; + else if (gotdot) + break; + else + gotdot = true; + } + else if (vr[end] < '0' || vr[end] > '9') + { + /* gone past digits and dots */ + break; + } + } + if (end == 0 || vr[end - 1] == '.' || !gotdot) + return NULL; + + vr[end] = '\0'; + return vr; +} + +/* + * make sure the data directory either doesn't exist or is empty + * + */ +static bool +check_data_dir(void) +{ + DIR *chkdir; + struct dirent *file; + bool empty = true; + + chkdir = opendir(pg_data); + + if (!chkdir) + return (errno == ENOENT); + + while ((file = readdir(chkdir)) != NULL) + { + if (strcmp(".", file->d_name) == 0 || strcmp("..", file->d_name) == 0) + { + /* skip this and parent directory */ + continue; + } + else + { + empty = false; + break; + } + } + + closedir(chkdir); + + return (empty); +} + +/* + * make the data directory (or one of its subdirectories if subdir is not NULL) + * + */ +static bool +mkdatadir(char *subdir) +{ + char *path; + int res; + + path = xmalloc(strlen(pg_data) + 2 + + (subdir == NULL ? 0 : strlen(subdir))); + + if (subdir != NULL) + sprintf(path, "%s/%s", pg_data, subdir); + else + strcpy(path, pg_data); + + res = mkdir(path, 0700); + if (res == 0) + return true; + else if (subdir == NULL || errno != ENOENT) + return false; + else + return !build_path(path, 0700); +} + + +/* + * set name of given input file variable under data directory + * + */ +static void +set_input(char **dest, char *filename) +{ + *dest = xmalloc(strlen(datadir) + strlen(filename) + 2); + sprintf(*dest, "%s/%s", datadir, filename); +} + +/* + * check that given input file exists + * + */ +static void +check_input(char *path) +{ + struct stat statbuf; + + if (stat(path, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) + { + fprintf(stderr, + "%s: file \"%s\" not found\n" + "This means you have a corrupted installation or identified\n" + "the wrong directory with the invocation option -L.\n", + progname, path); + exit(1); + } + +} + +/* + * TODO - clean this up and handle the errors properly + * don't overkill + * + */ + +#define FIND_SUCCESS 0 +#define FIND_NOT_FOUND 1 +#define FIND_STAT_ERR 2 +#define FIND_NOT_REGFILE 3 +#define FIND_BAD_PERM 4 +#define FIND_EXEC_ERR 5 +#define FIND_WRONG_VERSION 6 + +/* + * see if there is a postgres executable in the given path, and giving the + * right version number + * + */ +static int +find_postgres(char *path) +{ + char fn[MAXPGPATH]; + char cmd[MAXPGPATH]; + char line[100]; + +#ifndef WIN32 + + int permmask = S_IROTH | S_IXOTH; +#endif + + + struct stat statbuf; + FILE *pgver; + int plen = strlen(path); + + if (path[plen - 1] != '/') + snprintf(fn, MAXPGPATH, "%s/postgres%s", path, EXE); + else + snprintf(fn, MAXPGPATH, "%spostgres%s", path, EXE); + + if (stat(fn, &statbuf) != 0) + { + if (errno == ENOENT) + return FIND_NOT_FOUND; + else + return FIND_STAT_ERR; + } + if (!S_ISREG(statbuf.st_mode)) + return FIND_NOT_REGFILE; + +#ifndef WIN32 + + /* on windows a .exe file should be executable - this is the unix test */ + + if ((statbuf.st_mode & permmask) != permmask) + return FIND_BAD_PERM; +#endif /* ! WIN32 */ + + snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL); + + if ((pgver = popen(cmd, "r")) == NULL) + return FIND_EXEC_ERR; + + if (fgets(line, sizeof(line), pgver) == NULL) + { + perror("fgets failure"); + + } + + pclose(pgver); + + if (strcmp(line, PG_VERSIONSTR) != 0) + return FIND_WRONG_VERSION; + + return FIND_SUCCESS; +} + +#ifdef WIN32 + +/* + * Windows doesn't like relative paths to executables (other things work fine) + * so we call its builtin function to expand them. Elsewhere this is a NOOP + * + */ + +static char * +expanded_path(char *path) +{ + char abspath[MAXPGPATH]; + + if (_fullpath(abspath, path, MAXPGPATH) == NULL) + { + perror("expanded path"); + return path; + } + canonicalise_path(abspath); + return xstrdup(abspath); +} +#endif /* WIN32 */ + +/* + * set the paths pointing to postgres + * look for it in the same place we found this program, or in the environment + * path, or in the configured bindir. + * + */ +static int +set_paths(void) +{ + if (testpath && !self_path) + { + + char *path, + *cursor; + int pathlen, + i, + pathsegs; + char **pathbits; + char buf[MAXPGPATH]; + struct stat statbuf; + + path = xstrdup(getenv("PATH")); + pathlen = strlen(path); + + for (i = 0, pathsegs = 1; i < pathlen; i++) + { + if (path[i] == PATHSEP) + pathsegs++; + } + + pathbits = (char **) xmalloc(pathsegs * sizeof(char *)); + for (i = 0, pathsegs = 0, cursor = path; i <= pathlen; i++) + { + if (path[i] == PATHSEP || path[i] == 0) + { + path[i] = 0; + if (strlen(cursor) == 0) + { + /* empty path segment means current directory */ + pathbits[pathsegs] = xstrdup("."); + } + else + { + canonicalise_path(cursor); + pathbits[pathsegs] = cursor; + } + pathsegs++; + cursor = path + i + 1; + } + } + + for (i = 0; i < pathsegs; i++) + { + snprintf(buf, MAXPGPATH, "%s/%s%s", pathbits[i], progname, EXE); + if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) + { + self_path = pathbits[i]; + break; + } + } + } + + if (testpath && self_path && + (find_postgres(expanded_path(self_path)) == 0)) + { + /* we found postgres on out own path */ + pgpath = expanded_path(self_path); + } + else + { + /* look in the hardcoded bindir */ + int res; + char *cbindir; + + cbindir = xstrdup(bindir); + canonicalise_path(cbindir); + res = find_postgres(expanded_path(cbindir)); + if (res == 0) + pgpath = expanded_path(cbindir); + else + return 1; + } + + return 0; +} + +/* + * write out the PG_VERSION file in the data dir, or its subdirectory + * if extrapath is not NULL + * + */ +static void +set_short_version(char *short_version, char *extrapath) +{ + FILE *version_file; + char *path; + + if (extrapath == NULL) + { + path = xmalloc(strlen(pg_data) + 12); + sprintf(path, "%s/PG_VERSION", pg_data); + } + else + { + path = xmalloc(strlen(pg_data) + strlen(extrapath) + 13); + sprintf(path, "%s/%s/PG_VERSION", pg_data, extrapath); + } + version_file = fopen(path, PG_BINARY_W); + fprintf(version_file, "%s\n", short_version); + fclose(version_file); +} + +/* + * set up an empty config file so we can check buffers and connections + * + */ +static void +set_null_conf(void) +{ + FILE *conf_file; + char *path; + + path = xmalloc(strlen(pg_data) + 17); + sprintf(path, "%s/postgresql.conf", pg_data); + conf_file = fopen(path, PG_BINARY_W); + fclose(conf_file); +} + +/* + * check how many buffers we can run with + * + */ +static void +test_buffers(void) +{ + char *format = + "\"%s/postgres\" -boot -x 0 -F " + "-c shared_buffers=%d -c max_connections=5 template1 <%s >%s 2>&1"; + char cmd[MAXPGPATH]; + int bufs[] = + {1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 50}; + int len = sizeof(bufs) / sizeof(int); + int i, + status; + + for (i = 0; i < len; i++) + { + snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], DEVNULL, DEVNULL); + status = system(cmd); + if (status == 0) + break; + } + if (i >= len) + i = len - 1; + n_buffers = bufs[i]; + printf("buffers set to %d\n", n_buffers); +} + +/* + * check how many connections we can sustain + * + */ +static void +test_connections(void) +{ + char *format = + "\"%s/postgres\" -boot -x 0 -F " + "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1"; + char cmd[MAXPGPATH]; + int conns[] = {100, 50, 40, 30, 20, 10}; + int len = sizeof(conns) / sizeof(int); + int i, + status; + + for (i = 0; i < len; i++) + { + snprintf(cmd, sizeof(cmd), format, + pgpath, n_buffers, conns[i], DEVNULL, DEVNULL); + status = system(cmd); + if (status == 0) + break; + } + if (i >= len) + i = len - 1; + n_connections = conns[i]; + printf("connections set to %d\n", n_connections); +} + +/* + * set up all the config files + * + */ +static void +setup_config(void) +{ + + char **conflines; + char repltok[100]; + char path[MAXPGPATH]; + + fputs("creating configuration files ... ", stdout); + + /* postgresql.conf */ + + conflines = readfile(conf_file); + + snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers); + conflines = replace_token(conflines, "#shared_buffers = 1000", repltok); + + snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections); + conflines = replace_token(conflines, "#max_connections = 100", repltok); + + snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages); + conflines = replace_token(conflines, "#lc_messages = 'C'", repltok); + + snprintf(repltok, sizeof(repltok), "lc_monetary = '%s'", lc_monetary); + conflines = replace_token(conflines, "#lc_monetary = 'C'", repltok); + + snprintf(repltok, sizeof(repltok), "lc_numeric = '%s'", lc_numeric); + + conflines = replace_token(conflines, "#lc_numeric = 'C'", repltok); + + snprintf(repltok, sizeof(repltok), "lc_time = '%s'", lc_time); + conflines = replace_token(conflines, "#lc_time = 'C'", repltok); + + snprintf(path, MAXPGPATH, "%s/postgresql.conf", pg_data); + + writefile(path, conflines); + chmod(path, 0600); + + free(conflines); + + + /* pg_hba.conf */ + + conflines = readfile(hba_file); + +#ifndef HAVE_IPV6 + conflines = replace_token(conflines, + "host all all ::1", + "#host all all ::1"); +#endif /* ! HAVE_IPV6 */ + + snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data); + + writefile(path, conflines); + chmod(path, 0600); + + free(conflines); + + /* pg_ident.conf */ + + conflines = readfile(ident_file); + + snprintf(path, MAXPGPATH, "%s/pg_ident.conf", pg_data); + + writefile(path, conflines); + chmod(path, 0600); + + free(conflines); + + check_ok(); + +} + + +/* + * run the bootstrap code + * + */ +static void +bootstrap_template1(char *short_version) +{ + char *talkargs = ""; + char **bki_lines; + char headerline[MAXPGPATH]; + + PG_CMD_DECL; + + printf("creating template1 database in %s/base/1 ... ", pg_data); + + if (debug) + talkargs = "-d 5"; + + bki_lines = readfile(bki_file); + + snprintf(headerline, MAXPGPATH, "# PostgreSQL %s\n", short_version); + + if (strcmp(headerline, *bki_lines) != 0) + { + fprintf(stderr, + "%s: input file \"%s\" does not belong to PostgreSQL %s\n" + "Check your installation or specify the correct path " + "using the option -L.\n", + progname, bki_file, PG_VERSION); + exit_nicely(); + + } + + bki_lines = replace_token(bki_lines, "POSTGRES", effective_user); + + bki_lines = replace_token(bki_lines, "ENCODING", encodingid); + + /* + * we could save the old environment here, and restore it afterwards, + * but there doesn't seem to be any point, especially as we have + * already called setlocale(). + * + */ + + snprintf(cmd, MAXPGPATH, "LC_COLLATE=%s", lc_collate); + putenv(xstrdup(cmd)); + + snprintf(cmd, MAXPGPATH, "LC_CTYPE=%s", lc_ctype); + putenv(xstrdup(cmd)); + + putenv("LC_ALL"); + + snprintf(cmd, MAXPGPATH, + " \"%s/postgres\" -boot -x1 -F %s template1", pgpath, talkargs); + + PG_CMD_OPEN; + + for (line = bki_lines; *line != NULL; line++) + { + PG_CMD_PUTLINE; + free(*line); + } + + PG_CMD_CLOSE; + + free(bki_lines); + + check_ok(); +} + +/* + * set up the shadow password table + * + */ +static void +setup_shadow(void) +{ + + char *pg_shadow_setup[] = { + /* + * Create a trigger so that direct updates to pg_shadow will be + * written to the flat password/group files pg_pwd and pg_group + */ + "CREATE TRIGGER pg_sync_pg_pwd " + " AFTER INSERT OR UPDATE OR DELETE ON pg_shadow " + " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", + "CREATE TRIGGER pg_sync_pg_group " + " AFTER INSERT OR UPDATE OR DELETE ON pg_group " + " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", + + /* + * needs to be done before alter user, because alter user checks + * that pg_shadow is secure ... + */ + "REVOKE ALL on pg_shadow FROM public;\n", + NULL + }; + + PG_CMD_DECL; + + fputs("initializing pg_shadow ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_shadow_setup; *line != NULL; line++) + PG_CMD_PUTLINE; + + PG_CMD_CLOSE; + + check_ok(); +} + +/* + * get the superuser password if required, and call postgres to set it + * + */ +static void +get_set_pw(void) +{ + PG_CMD_DECL_NOLINE; + + char *pw1, + *pw2; + char pwpath[MAXPGPATH]; + struct stat statbuf; + + pw1 = simple_prompt("Enter new superuser password: ", 100, false); + pw2 = simple_prompt("Enter it again: ", 100, false); + if (strcmp(pw1, pw2) != 0) + { + fprintf(stderr, "Passwords didn't match.\n"); + exit_nicely(); + } + free(pw2); + + printf("storing the password ... "); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); + + + PG_CMD_OPEN; + + if (fprintf( + pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pw1) < 0) + { + /* write failure */ + exit_nicely(); + } + fflush(pg); + + PG_CMD_CLOSE; + + snprintf(pwpath, MAXPGPATH, "%s/global/pg_pwd", pg_data); + if (stat(pwpath, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) + { + fprintf(stderr, + "%s: The password file was not generated - " + "please report this problem\n", + progname); + exit_nicely(); + } + + check_ok(); +} + +/* + * toast sys tables + * + */ +static void +unlimit_systables(void) +{ + char *systables_setup[] = { + "ALTER TABLE pg_attrdef CREATE TOAST TABLE;\n", + "ALTER TABLE pg_constraint CREATE TOAST TABLE;\n", + "ALTER TABLE pg_database CREATE TOAST TABLE;\n", + "ALTER TABLE pg_description CREATE TOAST TABLE;\n", + "ALTER TABLE pg_group CREATE TOAST TABLE;\n", + "ALTER TABLE pg_proc CREATE TOAST TABLE;\n", + "ALTER TABLE pg_rewrite CREATE TOAST TABLE;\n", + "ALTER TABLE pg_shadow CREATE TOAST TABLE;\n", + "ALTER TABLE pg_statistic CREATE TOAST TABLE;\n", + NULL + }; + + PG_CMD_DECL; + + fputs("enabling unlimited row size for system tables ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); + + PG_CMD_OPEN; + + for (line = systables_setup; *line != NULL; line++) + PG_CMD_PUTLINE; + + PG_CMD_CLOSE; + + check_ok(); +} + +/* + * set up pg_depend + * + */ +static void +setup_depend(void) +{ + char *pg_depend_setup[] = { + /* + * Make PIN entries in pg_depend for all objects made so far in + * the tables that the dependency code handles. This is overkill + * (the system doesn't really depend on having every last weird + * datatype, for instance) but generating only the minimum + * required set of dependencies seems hard. Note that we + * deliberately do not pin the system views. First delete any + * already-made entries; PINs override all else, and must be the + * only entries for their objects. + */ + "DELETE FROM pg_depend;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_class;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_proc;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_type;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_cast;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_constraint;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_attrdef;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_language;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_operator;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_opclass;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_rewrite;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_trigger;\n", + + /* + * restriction here to avoid pinning the public namespace + */ + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_namespace " + " WHERE nspname LIKE 'pg%';\n", + NULL + }; + + PG_CMD_DECL; + + fputs("initializing pg_depend ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_depend_setup; *line != NULL; line++) + PG_CMD_PUTLINE; + + PG_CMD_CLOSE; + + check_ok(); +} + +/* + * set up system views + * + */ +static void +setup_sysviews(void) +{ + PG_CMD_DECL; + + char **sysviews_setup; + + fputs("creating system views ... ", stdout); + + sysviews_setup = readfile(system_views_file); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -N -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + for (line = sysviews_setup; *line != NULL; line++) + { + PG_CMD_PUTLINE; + free(*line); + } + + PG_CMD_CLOSE; + + free(sysviews_setup); + + check_ok(); +} + +/* + * load description data + * + */ +static void +setup_description(void) +{ + char *pg_description_setup1[] = { + "CREATE TEMP TABLE tmp_pg_description ( " + " objoid oid, " + " classname name, " + " objsubid int4, " + " description text) WITHOUT OIDS;\n", + "COPY tmp_pg_description FROM STDIN;\n", + NULL + }; + + char *pg_description_setup2[] = { + "\\.\n", + "INSERT INTO pg_description " + " SELECT t.objoid, c.oid, t.objsubid, t.description " + " FROM tmp_pg_description t, pg_class c " + " WHERE c.relname = t.classname;\n", + NULL + }; + + + PG_CMD_DECL; + + char **desc_lines; + + fputs("loading pg_description ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_description_setup1; *line != NULL; line++) + PG_CMD_PUTLINE; + + desc_lines = readfile(desc_file); + for (line = desc_lines; *line != NULL; line++) + { + PG_CMD_PUTLINE; + free(*line); + } + + free(desc_lines); + + + for (line = pg_description_setup2; *line != NULL; line++) + PG_CMD_PUTLINE; + + PG_CMD_CLOSE; + + check_ok(); +} + +/* + * load conversion functions + * + */ +static void +setup_conversion(void) +{ + PG_CMD_DECL; + + char **conv_lines; + + fputs("creating conversions ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + conv_lines = readfile(conversion_file); + for (line = conv_lines; *line != NULL; line++) + { + if (strstr(*line, "DROP CONVERSION") != *line) + PG_CMD_PUTLINE; + + free(*line); + } + + free(conv_lines); + + PG_CMD_CLOSE; + + check_ok(); +} + +/* + * run privileges script + * + */ +static void +setup_privileges(void) +{ + char *privileges_setup[] = { + "UPDATE pg_class " + " SET relacl = '{\"=r/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " + " WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n", + "UPDATE pg_proc " + " SET proacl = '{\"=X/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " + " WHERE proacl IS NULL;\n", + "UPDATE pg_language " + " SET lanacl = '{\"=U/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " + " WHERE lanpltrusted;\n", + "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n", + "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n", + NULL + }; + + PG_CMD_DECL; + + char **priv_lines; + + fputs("setting privileges on builtin objects ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + priv_lines = replace_token(privileges_setup, + "$POSTGRES_SUPERUSERNAME", username); + for (line = priv_lines; *line != NULL; line++) + PG_CMD_PUTLINE; + + PG_CMD_CLOSE; + + check_ok(); +} + +/* + * extract the strange version of version required for info schema + * + */ +static void +set_info_version(void) +{ + char *letterversion; + long major = 0, + minor = 0, + micro = 0; + char *endptr; + char *vstr = xstrdup(PG_VERSION); + char *ptr; + + ptr = vstr + (strlen(vstr) - 1); + while (ptr != vstr && (*ptr < '0' || *ptr > '9')) + ptr--; + letterversion = ptr + 1; + major = strtol(vstr, &endptr, 10); + if (*endptr) + minor = strtol(endptr + 1, &endptr, 10); + if (*endptr) + micro = strtol(endptr + 1, &endptr, 10); + snprintf(infoversion, sizeof(infoversion), "%02ld.%02ld.%04ld%s", + major, minor, micro, letterversion); +} + +/* + * load info schema and populate from features file + * + */ +static void +setup_schema(void) +{ + + PG_CMD_DECL; + + char **lines; + int fres; + + fputs("creating information schema ... ", stdout); + + lines = readfile(info_schema_file); + + /* + * note that here we don't run in single line mode, unlike other + * places + */ + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true -N template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + for (line = lines; *line != NULL; line++) + { + PG_CMD_PUTLINE; + free(*line); + } + + free(lines); + + PG_CMD_CLOSE; + + lines = readfile(features_file); + + /* + * strip CR before NL this is the only place we do this (following + * the shell script) - we could do it universally in readfile() if + * necessary + * + */ + lines = replace_token(lines, "\r\n", "\n"); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + fres = fprintf(pg, + "UPDATE information_schema.sql_implementation_info " + " SET character_value = '%s' " + " WHERE implementation_info_name = 'DBMS VERSION';\n", + infoversion); + + if (fres < 0) + exit_nicely(); + + fres = fputs("COPY information_schema.sql_features " + " (feature_id, feature_name, sub_feature_id, " + " sub_feature_name, is_supported, comments) " + "FROM STDIN;\n", + pg); + + if (fres < 0) + exit_nicely(); + + for (line = lines; *line != NULL; line++) + { + PG_CMD_PUTLINE; + free(*line); + } + + free(lines); + + if (fputs("\\.\n", pg) < 0) + exit_nicely(); + fflush(pg); + + PG_CMD_CLOSE; + + check_ok(); +} + +/* + * clean everything up in template1 + * + */ +static void +vacuum_db(void) +{ + PG_CMD_DECL_NOLINE; + + fputs("vacuuming database template1 ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + if (fputs("ANALYSE;\nVACUUM FULL FREEZE;\n", pg) < 0) + exit_nicely(); + fflush(pg); + + PG_CMD_CLOSE; + + check_ok(); + +} + +/* + * copy template1 to template0 + * + */ +static void +make_template0(void) +{ + char *template0_setup[] = { + "CREATE DATABASE template0;\n", + "UPDATE pg_database SET " + " datistemplate = 't', " + " datallowconn = 'f' " + " WHERE datname = 'template0';\n", + + /* + * We use the OID of template0 to determine lastsysoid + * + */ + "UPDATE pg_database SET datlastsysoid = " + " (SELECT oid::int4 - 1 FROM pg_database " + " WHERE datname = 'template0');\n", + + /* + * Explicitly revoke public create-schema and create-temp-table + * privileges in template1 and template0; else the latter would be + * on by default + */ + "REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;\n", + "REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;\n", + + /* + * Finally vacuum to clean up dead rows in pg_database + */ + "VACUUM FULL pg_database;\n", + NULL + }; + + PG_CMD_DECL; + + fputs("copying template1 to template0 ... ", stdout); + + snprintf(cmd, MAXPGPATH, + "\"%s/postgres\" -F -O -c search_path=pg_catalog " + "-c exit_on_error=true template1 >%s", + pgpath, DEVNULL); + + PG_CMD_OPEN; + + for (line = template0_setup; *line; line++) + PG_CMD_PUTLINE; + + PG_CMD_CLOSE; + + check_ok(); +} + + +/* + * signal handler in case we are interrupted. + * + * The Windows runtime docs at + * http://msdn.microsoft.com/library/en-us/vclib/html/_crt_signal.asp + * specifically forbid a number of things being done from a signal handler, + * including IO, memory allocation and system calls, and only allow jmpbuf + * if you are handling SIGFPE. + * + * I avoided doing the forbidden things by setting a flag instead of calling + * exit_nicely() directly. + * + * Also note the behaviour of Windows with SIGINT, which says this: + * Note SIGINT is not supported for any Win32 application, including + * Windows 98/Me and Windows NT/2000/XP. When a CTRL+C interrupt occurs, + * Win32 operating systems generate a new thread to specifically handle + * that interrupt. This can cause a single-thread application such as UNIX, + * to become multithreaded, resulting in unexpected behavior. + * + * I have no idea how to handle this. (Strange they call UNIX an application!) + * So this will need some testing on Windows. + * + */ + +static void +trapsig(int signum) +{ + /* handle systems that reset the handler, like Windows (grr) */ + pqsignal(signum, trapsig); + not_ok = true; +} + +/* + * call exit_nicely() if we got a signal, or else output "ok". + * + */ +static void +check_ok() +{ + if (not_ok) + { + puts("Caught Signal."); + exit_nicely(); + } + else + { + /* no signal caught */ + puts("ok"); + } +} + + +/* + * check if given string is a valid locle specifier + * based on some code given to me by Peter Eisentraut + * (but I take responsibility for it :-) + */ +static bool +chklocale(const char *locale) +{ + bool ret; + int category = LC_CTYPE; + char *save; + + save = setlocale(category, NULL); + if (!save) + return false; /* should not happen; */ + + save = xstrdup(save); + + ret = (setlocale(category, locale) != NULL); + + setlocale(category, save); + free(save); + + /* should we exit here? */ + if (!ret) + fprintf(stderr, "%s: invalid locale name \"%s\"\n", progname, locale); + + return ret; +} + +/* + * set up the locale variables + * assumes we have called setlocale(LC_ALL,"") + * + */ +static void +setlocales(void) +{ + /* set empty lc_* values to locale config if set */ + + if (strlen(locale) > 0) + { + if (strlen(lc_ctype) == 0) + lc_ctype = locale; + if (strlen(lc_collate) == 0) + lc_collate = locale; + if (strlen(lc_numeric) == 0) + lc_numeric = locale; + if (strlen(lc_time) == 0) + lc_time = locale; + if (strlen(lc_monetary) == 0) + lc_monetary = locale; + if (strlen(lc_messages) == 0) + lc_messages = locale; + } + + /* + * override absent/invalid config settings from initdb's locale + * settings + */ + + if (strlen(lc_ctype) == 0 || !chklocale(lc_ctype)) + lc_ctype = xstrdup(setlocale(LC_CTYPE, NULL)); + if (strlen(lc_collate) == 0 || !chklocale(lc_collate)) + lc_collate = xstrdup(setlocale(LC_COLLATE, NULL)); + if (strlen(lc_numeric) == 0 || !chklocale(lc_numeric)) + lc_numeric = xstrdup(setlocale(LC_NUMERIC, NULL)); + if (strlen(lc_time) == 0 || !chklocale(lc_time)) + lc_time = xstrdup(setlocale(LC_TIME, NULL)); + if (strlen(lc_monetary) == 0 || !chklocale(lc_monetary)) + lc_monetary = xstrdup(setlocale(LC_MONETARY, NULL)); + if (strlen(lc_messages) == 0 || !chklocale(lc_messages)) +#ifdef LC_MESSAGES + { + /* when available get the current locale setting */ + lc_messages = xstrdup(setlocale(LC_MESSAGES, NULL)); + } +#else + { + /* when not available, get the CTYPE setting */ + lc_messages = xstrdup(setlocale(LC_CTYPE, NULL)); + } +#endif /* LC_MESSAGES */ + +} + +/* + * help text data + * + */ + +char *usage_text[] = { + "$CMDNAME initializes a PostgreSQL database cluster.\n", + "\n", + "Usage:\n", + " $CMDNAME [OPTION]... [DATADIR]\n", + "\n", + "Options:\n", + " [-D, --pgdata=]DATADIR location for this database cluster\n", + " -E, --encoding=ENCODING set default encoding for new databases\n", + " --locale=LOCALE initialize database cluster with given locale\n", + " --lc-collate, --lc-ctype, --lc-messages=LOCALE\n", + " --lc-monetary, --lc-numeric, --lc-time=LOCALE\n", + " initialize database cluster with given locale\n", + " in the respective category (default taken from\n", + " environment)\n", + " --no-locale equivalent to --locale=C\n", + " -U, --username=NAME database superuser name\n", + " -W, --pwprompt prompt for a password for the new superuser\n", + " -?, --help show this help, then exit\n", + " -V, --version output version information, then exit\n", + "\n", + "Less commonly used options: \n", + " -d, --debug generate lots of debugging output\n", + " -s, --show show internal settings\n", + " -L DIRECTORY where to find the input files\n", + " -n, --noclean do not clean up after errors\n", + "\n", + "If the data directory is not specified, the environment variable PGDATA\n", + "is used.\n", + "\n", + "Report bugs to .\n", + NULL +}; + + + +/* + * print help text + * + */ +static void +usage(void) +{ + + int i; + char **newtext; + + newtext = replace_token(usage_text, "$CMDNAME", progname); + + for (i = 0; newtext[i]; i++) + fputs(newtext[i], stdout); /* faster than printf */ +} + +int +main(int argc, char *argv[]) +{ + /* + * options with no short version return a low integer, the rest return + * their short version value + * + */ + static struct option long_options[] = { + {"pgdata", required_argument, NULL, 'D'}, + {"encoding", required_argument, NULL, 'E'}, + {"locale", required_argument, NULL, 1}, + {"lc-collate", required_argument, NULL, 2}, + {"lc-ctype", required_argument, NULL, 3}, + {"lc-monetary", required_argument, NULL, 4}, + {"lc-numeric", required_argument, NULL, 5}, + {"lc-time", required_argument, NULL, 6}, + {"lc-messages", required_argument, NULL, 7}, + {"no-locale", no_argument, NULL, 8}, + {"pwprompt", no_argument, NULL, 'W'}, + {"username", required_argument, NULL, 'U'}, + {"help", no_argument, NULL, '?'}, + {"version", no_argument, NULL, 'V'}, + {"debug", no_argument, NULL, 'd'}, + {"show", no_argument, NULL, 's'}, + {"noclean", no_argument, NULL, 'n'}, + {0, 0, 0, 0} + }; + + int c, + i; + int option_index; + char *short_version; + char *pgdenv; /* PGDATA value got from sent to + * environment */ + char *subdirs[] = + {"global", "pg_xlog", "pg_clog", "base", "base/1"}; + + char *lastsep; + + /* parse argv[0] - detect explicit path if there was one */ + + char *carg0; + +#if defined(__CYGWIN__) || defined(WIN32) + char *exe; /* location of exe suffix in progname */ +#endif /* defined(__CYGWIN__) || defined(WIN32) */ + + setlocale(LC_ALL, ""); + + carg0 = xstrdup(argv[0]); + canonicalise_path(carg0); + + lastsep = strrchr(carg0, '/'); + progname = lastsep ? xstrdup(lastsep + 1) : carg0; + +#if defined(__CYGWIN__) || defined(WIN32) + if (strlen(progname) > 4 && + (exe = progname + (strlen(progname) - 4)) && + stricmp(exe, EXE) == 0) + { + /* strip .exe suffix, regardless of case */ + *exe = '\0'; + } +#endif /* defined(__CYGWIN__) || defined(WIN32) */ + + if (lastsep) + { + self_path = carg0; + *lastsep = '\0'; + } + else + { + /* no path known to ourselves from argv[0] */ + self_path = NULL; + } + + /* process options */ + + while (1) + { + /* + * a : as the first option char here lets us use ? as a short + * option + */ + c = getopt_long(argc, argv, ":D:E:WU:?sVdnL:", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) + { + case 'D': + pg_data = xstrdup(optarg); + break; + case 'E': + encoding = xstrdup(optarg); + break; + case 'W': + pwprompt = true; + break; + case 'U': + username = xstrdup(optarg); + break; + case 'd': + debug = true; + break; + case 'n': + noclean = true; + break; + case 'L': + datadir = xstrdup(optarg); + break; + case 1: + locale = xstrdup(optarg); + break; + case 2: + lc_collate = xstrdup(optarg); + break; + case 3: + lc_ctype = xstrdup(optarg); + break; + case 4: + lc_monetary = xstrdup(optarg); + break; + case 5: + lc_numeric = xstrdup(optarg); + break; + case 6: + lc_time = xstrdup(optarg); + break; + case 7: + lc_messages = xstrdup(optarg); + break; + case 8: + locale = "C"; + break; + case '?': + show_help = true; + break; + case 's': + show_setting = true; + break; + case 'V': + show_version = true; + break; + default: + show_help = true; + printf("Unrecognized option: %c\n", c); + } + + } + + if (optind < argc) + { + pg_data = xstrdup(argv[optind]); + optind++; + } + + set_info_version(); + + if (strlen(pg_data) == 0 && !(show_help || show_setting)) + { + pgdenv = getenv("PGDATA"); + if (pgdenv && strlen(pgdenv)) + { + /* PGDATA found */ + pg_data = xstrdup(pgdenv); + } + else + { + fprintf(stderr, + "%s: no data directory specified\n" + "You must identify the directory where the data for this " + "database system\n" + "will reside. Do this with either the invocation option " + "-D or the\n" "environment variable PGDATA.\n", + progname); + } + } + + canonicalise_path(pg_data); + + /* + * we have to set PGDATA for postgres rather than pass it on the + * commnd line to avoid dumb quoting problems on Windows, and we would + * expecially need quotes otherwise on Windows because paths there are + * most likely to have embedded spaces. + * + */ + pgdenv = xmalloc(8 + strlen(pg_data)); + sprintf(pgdenv, "PGDATA=%s", pg_data); + putenv(pgdenv); + + if (optind < argc) + show_help = true; + + if (show_version) + { + /* hard coded name here, in case they rename executable */ + printf("initdb (PostgreSQL) %s\n", PG_VERSION); + exit(0); + } + + if (show_help) + { + usage(); + exit(0); + } + + if (set_paths() != 0) + { + fprintf(stderr, + "The program \"postgres\" is needed by %s " + "but was not found in \n" + "the directory \"%s\". Check your installation.\n", + progname, bindir); + exit(1); + + } + + + if ((short_version = get_short_version()) == NULL) + { + fprintf(stderr, "%s: could not get valid short version\n", progname); + exit(1); + } + + effective_user = get_id(); + if (!strlen(username)) + username = effective_user; + + if (strlen(encoding)) + encodingid = get_encoding_id(encoding); + + set_input(&bki_file, "postgres.bki"); + set_input(&desc_file, "postgres.description"); + set_input(&hba_file, "pg_hba.conf.sample"); + set_input(&ident_file, "pg_ident.conf.sample"); + set_input(&conf_file, "postgresql.conf.sample"); + set_input(&conversion_file, "conversion_create.sql"); + set_input(&info_schema_file, "information_schema.sql"); + set_input(&features_file, "sql_features.txt"); + set_input(&system_views_file, "system_views.sql"); + + + if (show_setting || debug) + { + fprintf(stderr, + "VERSION=%s\n" + "PGDATA=%s\ndatadir=%s\nPGPATH=%s\n" + "ENCODING=%s\nENCODINGID=%s\n" + "POSTGRES_SUPERUSERNAME=%s\nPOSTGRES_BKI=%s\n" + "POSTGRES_DESCR=%s\nPOSTGRESQL_CONF_SAMPLE=%s\n" + "PG_HBA_SAMPLE=%s\nPG_IDENT_SAMPLE=%s\n", + PG_VERSION, + pg_data, datadir, pgpath, + encoding, encodingid, + username, bki_file, + desc_file, conf_file, + hba_file, ident_file); + } + + + if (show_setting) + exit(0); + + + check_input(bki_file); + check_input(desc_file); + check_input(hba_file); + check_input(ident_file); + check_input(conf_file); + check_input(conversion_file); + check_input(info_schema_file); + check_input(features_file); + check_input(system_views_file); + + setlocales(); + + if (strcmp(lc_ctype, lc_collate) == 0 && + strcmp(lc_ctype, lc_time) == 0 && + strcmp(lc_ctype, lc_numeric) == 0 && + strcmp(lc_ctype, lc_monetary) == 0 && + strcmp(lc_ctype, lc_messages) == 0) + { + printf("The database cluster will be initialized with locale %s\n", + lc_ctype); + } + else + { + printf("The database cluster will be initialized with locales\n" + " COLLATE: %s\n" + " CTYPE: %s\n" + " MESSAGES: %s\n" + " MONETARY: %s\n" + " NUMERIC: %s\n" + " TIME: %s\n", + lc_collate, + lc_ctype, + lc_messages, + lc_monetary, + lc_numeric, + lc_time); + } + + umask(077); + + /* + * now we are starting to do real work, trap signals so we can clean + * up + */ + + /* some of these are not valid on Windows */ +#ifdef SIGHUP + pqsignal(SIGHUP, trapsig); +#endif /* SIGHUP */ +#ifdef SIGINT + pqsignal(SIGINT, trapsig); +#endif /* SIGINT */ +#ifdef SIGQUIT + pqsignal(SIGQUIT, trapsig); +#endif /* SIGQUIT */ +#ifdef SIGTERM + pqsignal(SIGTERM, trapsig); +#endif /* SIGTERM */ + + + /* clear this we'll use it in a few lines */ + errno = 0; + + if (!check_data_dir()) + { + fprintf(stderr, + "%s: directory \"%s\" exists but is not empty\n" + "If you want to create a new database system, either " + "remove or empty\n" + "the directory \"$PGDATA\" or run $CMDNAME with an " + "argument other than\n" + "\"%s\".\n", + progname, pg_data, pg_data); + exit(1); + } + + /* + * check_data_dir() called opendir - the errno should still be hanging + * around + */ + + if (errno == ENOENT) + { + printf("creating directory \"%s\" ... ", pg_data); + + if (!mkdatadir(NULL)) + exit_nicely(); + else + check_ok(); + + made_new_pgdata = true; + } + + for (i = 0; i < (sizeof(subdirs) / sizeof(char *)); i++) + { + printf("creating directory %s/%s ... ", pg_data, subdirs[i]); + + if (!mkdatadir(subdirs[i])) + exit_nicely(); + else + check_ok(); + } + + set_short_version(short_version, NULL); + + set_null_conf(); + + test_buffers(); + + test_connections(); + + setup_config(); + + bootstrap_template1(short_version); + + set_short_version(short_version, "base/1"); + + setup_shadow(); + + if (pwprompt) + get_set_pw(); + + unlimit_systables(); + + setup_depend(); + + setup_sysviews(); + + setup_description(); + + setup_conversion(); + + setup_privileges(); + + setup_schema(); + + vacuum_db(); + + make_template0(); + + printf("\n" + "Success. You can now start the database server using:\n" + "\n" + " \"%s/postmaster\" -D \"%s\"\n" + "or\n" + " \"%s/pg_ctl\" -D \"%s\" -l logfile start\n" + "\n", + pgpath, pg_data, pgpath, pg_data); + + return 0; +} diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh deleted file mode 100644 index ed396c4bc2..0000000000 --- a/src/bin/initdb/initdb.sh +++ /dev/null @@ -1,1155 +0,0 @@ -#!@SHELL@ -#------------------------------------------------------------------------- -# -# initdb creates (initializes) a PostgreSQL database cluster (site, -# instance, installation, whatever). A database cluster is a -# collection of PostgreSQL databases all managed by the same postmaster. -# -# To create the database cluster, we create the directory that contains -# all its data, create the files that hold the global tables, create -# a few other control files for it, and create two databases: the -# template0 and template1 databases. -# -# The template databases are ordinary PostgreSQL databases. template0 -# is never supposed to change after initdb, whereas template1 can be -# changed to add site-local standard data. Either one can be copied -# to produce a new database. -# -# To create template1, we run the postgres (backend) program in bootstrap -# mode and feed it data from the postgres.bki library file. After this -# initial bootstrap phase, some additional stuff is created by normal -# SQL commands fed to a standalone backend. Those commands are just -# embedded into this script (yeah, it's ugly). -# -# template0 is made just by copying the completed template1. -# -# -# Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group -# Portions Copyright (c) 1994, Regents of the University of California -# -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.204 2003/10/13 21:06:44 tgl Exp $ -# -#------------------------------------------------------------------------- - - -########################################################################## -# -# INITIALIZATION - -exit_nicely(){ - stty echo > /dev/null 2>&1 - echo 1>&2 - echo "$CMDNAME: failed" 1>&2 - if [ "$noclean" != yes ]; then - if [ "$made_new_pgdata" = yes ]; then - echo "$CMDNAME: removing data directory \"$PGDATA\"" 1>&2 - rm -rf "$PGDATA" || echo "$CMDNAME: failed" 1>&2 - fi - else - echo "$CMDNAME: data directory \"$PGDATA\" not removed at user's request" 1>&2 - fi - exit 1 -} - -pg_getlocale(){ - arg=$1 - unset ret - - for var in "PGLC_$arg" PGLOCALE LC_ALL "LC_$arg" LANG; do - varset=`eval echo '${'"$var"'+set}'` - varval=`eval echo '$'"$var"` - if test "$varset" = set; then - ret=$varval - break - fi - done - - if test "${ret+set}" != set; then - ret=C - fi - - echo "$ret" -} - - -CMDNAME=`basename $0` - -# Placed here during build -VERSION='@VERSION@' -HAVE_IPV6='@HAVE_IPV6@' -bindir='@bindir@' -# Note that "datadir" is not the directory we're initializing, it's -# merely how Autoconf names PREFIX/share. -datadir='@datadir@' - - -# Check for echo -n vs echo \c -if echo '\c' | grep -s c >/dev/null 2>&1 -then - ECHO_N="echo -n" - ECHO_C="" -else - ECHO_N="echo" - ECHO_C='\c' -fi - - -# -# Find out where we're located -# -if echo "$0" | grep '/' > /dev/null 2>&1 -then - # explicit dir name given - self_path=`echo $0 | sed 's,/[^/]*$,,'` # (dirname command is not portable) -else - # look for it in PATH ('which' command is not portable) - for dir in `echo "$PATH" | sed 's/:/ /g'` - do - # empty entry in path means current dir - [ -z "$dir" ] && dir='.' - if [ -f "$dir/$CMDNAME" ] - then - self_path="$dir" - break - fi - done -fi - - -# Check for right version of backend. First we check for an -# executable in the same directory is this initdb script (presuming -# the above code worked). Then we fall back to the hard-wired bindir. -# We do it in this order because during upgrades users might move -# their trees to backup places, so $bindir might be inaccurate. - -if [ x"$self_path" != x"" ] \ - && [ -x "$self_path/postgres" ] \ - && [ x"`$self_path/postgres -V 2>/dev/null`" = x"postgres (PostgreSQL) $VERSION" ] -then - PGPATH=$self_path -elif [ -x "$bindir/postgres" ]; then - if [ x"`$bindir/postgres -V 2>/dev/null`" = x"postgres (PostgreSQL) $VERSION" ] - then - PGPATH=$bindir - else - # Maybe there was an error message? - errormsg=`$bindir/postgres -V 2>&1 >/dev/null` - ( - echo "The program " - echo " $bindir/postgres" - echo "needed by $CMDNAME does not belong to PostgreSQL version $VERSION, or" - echo "there may be a configuration problem." - if test x"$errormsg" != x""; then - echo - echo "This was the error message issued by that program:" - echo "$errormsg" - fi - ) 1>&2 - exit 1 - fi -else - echo "The program \"postgres\" is needed by $CMDNAME but was not found in" 1>&2 - echo "the directory \"$bindir\". Check your installation." 1>&2 - exit 1 -fi - - -# Now we can assume that 'pg_id' belongs to the same version as the -# verified 'postgres' in the same directory. -if [ ! -x "$PGPATH/pg_id" ]; then - echo "The program \"pg_id\" is needed by $CMDNAME but was not found in" 1>&2 - echo "the directory \"$PGPATH\". Check your installation." 1>&2 - exit 1 -fi - - -EffectiveUser=`$PGPATH/pg_id -n -u` -if [ -z "$EffectiveUser" ]; then - echo "$CMDNAME: could not determine current user name" 1>&2 - exit 1 -fi - -if [ `$PGPATH/pg_id -u` -eq 0 ] -then - echo "$CMDNAME: cannot be run as root" 1>&2 - echo "Please log in (using, e.g., \"su\") as the (unprivileged) user that will" 1>&2 - echo "own the server process." 1>&2 - exit 1 -fi - - -short_version=`echo $VERSION | sed -e 's!^\([0-9][0-9]*\.[0-9][0-9]*\).*!\1!'` -if [ x"$short_version" = x"" ] ; then - echo "$CMDNAME: bug: version number has wrong format" 1>&2 - exit 1 -fi - - -########################################################################## -# -# COMMAND LINE OPTIONS - -# 0 is the default (non-)encoding -ENCODINGID=0 - -# Set defaults: -debug= -noclean= -show_setting= - -# Note: There is a single compelling reason that the name of the database -# superuser be the same as the Unix user owning the server process: -# The single user postgres backend will only connect as the database -# user with the same name as the Unix user running it. That's -# a security measure. -POSTGRES_SUPERUSERNAME="$EffectiveUser" - -while [ "$#" -gt 0 ] -do - case "$1" in - --help|-\?) - usage=t - break - ;; - --version|-V) - echo "initdb (PostgreSQL) $VERSION" - exit 0 - ;; - --debug|-d) - debug=yes - echo "Running in debug mode." - ;; - --show|-s) - show_setting=yes - ;; - --noclean|-n) - noclean=yes - echo "Running in noclean mode. Mistakes will not be cleaned up." - ;; -# The name of the database superuser. Can be freely changed. - --username|-U) - POSTGRES_SUPERUSERNAME="$2" - shift;; - --username=*) - POSTGRES_SUPERUSERNAME=`echo $1 | sed 's/^--username=//'` - ;; - -U*) - POSTGRES_SUPERUSERNAME=`echo $1 | sed 's/^-U//'` - ;; -# The default password of the database superuser. -# Make initdb prompt for the default password of the database superuser. - --pwprompt|-W) - PwPrompt=1 - ;; -# Directory where to install the data. No default, unless the environment -# variable PGDATA is set. - --pgdata|-D) - PGDATA="$2" - shift;; - --pgdata=*) - PGDATA=`echo $1 | sed 's/^--pgdata=//'` - ;; - -D*) - PGDATA=`echo $1 | sed 's/^-D//'` - ;; -# The directory where the .bki input files are stored. Normally -# they are in PREFIX/share and this option should be unnecessary. - -L) - datadir="$2" - shift;; - -L*) - datadir=`echo $1 | sed 's/^-L//'` - ;; -# The encoding of the template1 database. Defaults to what you chose -# at configure time. (see above) - --encoding|-E) - ENCODING="$2" - shift;; - --encoding=*) - ENCODING=`echo $1 | sed 's/^--encoding=//'` - ;; - -E*) - ENCODING=`echo $1 | sed 's/^-E//'` - ;; -# Locale flags - --locale) - PGLOCALE="$2" - shift;; - --locale=*) - PGLOCALE=`echo $1 | sed 's/^[^=]*=//'` - ;; - --no-locale) - PGLOCALE=C - ;; - - --lc-collate) - PGLC_COLLATE=$2 - shift;; - --lc-collate=*) - PGLC_COLLATE=`echo $1 | sed 's/^[^=]*=//'` - ;; - --lc-ctype) - PGLC_CTYPE=$2 - shift;; - --lc-ctype=*) - PGLC_CTYPE=`echo $1 | sed 's/^[^=]*=//'` - ;; - --lc-messages) - PGLC_MESSAGES=$2 - shift;; - --lc-messages=*) - PGLC_MESSAGES=`echo $1 | sed 's/^[^=]*=//'` - ;; - --lc-monetary) - PGLC_MONETARY=$2 - shift;; - --lc-monetary=*) - PGLC_MONETARY=`echo $1 | sed 's/^[^=]*=//'` - ;; - --lc-numeric) - PGLC_NUMERIC=$2 - shift;; - --lc-numeric=*) - PGLC_NUMERIC=`echo $1 | sed 's/^[^=]*=//'` - ;; - --lc-time) - PGLC_TIME=$2 - shift;; - --lc-time=*) - PGLC_TIME=`echo $1 | sed 's/^[^=]*=//'` - ;; - - -*) - echo "$CMDNAME: invalid option: $1" - echo "Try \"$CMDNAME --help\" for more information." - exit 1 - ;; - -# Non-option argument specifies data directory - *) - PGDATA=$1 - ;; - esac - shift -done - -if [ "$usage" ]; then - echo "$CMDNAME initializes a PostgreSQL database cluster." - echo - echo "Usage:" - echo " $CMDNAME [OPTION]... [DATADIR]" - echo - echo "Options:" - echo " [-D, --pgdata=]DATADIR location for this database cluster" - echo " -E, --encoding=ENCODING set default encoding for new databases" - echo " --locale=LOCALE initialize database cluster with given locale" - echo " --lc-collate, --lc-ctype, --lc-messages=LOCALE" - echo " --lc-monetary, --lc-numeric, --lc-time=LOCALE" - echo " initialize database cluster with given locale" - echo " in the respective category (default taken from" - echo " environment)" - echo " --no-locale equivalent to --locale=C" - echo " -U, --username=NAME database superuser name" - echo " -W, --pwprompt prompt for a password for the new superuser" - echo " --help show this help, then exit" - echo " --version output version information, then exit" - echo - echo "Less commonly used options: " - echo " -d, --debug generate lots of debugging output" - echo " -L DIRECTORY where to find the input files" - echo " -n, --noclean do not clean up after errors" - echo - echo "If the data directory is not specified, the environment variable PGDATA" - echo "is used." - echo - echo "Report bugs to ." - exit 0 -fi - -#------------------------------------------------------------------------- -# Resolve the encoding name -#------------------------------------------------------------------------- - -if [ "$ENCODING" ] -then - ENCODINGID=`$PGPATH/pg_encoding -b $ENCODING` - if [ "$?" -ne 0 ] - then - ( - echo "$CMDNAME: pg_encoding failed" - echo "Make sure the program was installed correctly." - ) 1>&2 - exit 1 - fi - if [ -z "$ENCODINGID" ] - then - echo "$CMDNAME: \"$ENCODING\" is not a valid server encoding name" 1>&2 - exit 1 - fi -fi - - -#------------------------------------------------------------------------- -# Make sure he told us where to build the database system -#------------------------------------------------------------------------- - -if [ -z "$PGDATA" ] -then - ( - echo "$CMDNAME: no data directory specified" - echo "You must identify the directory where the data for this database system" - echo "will reside. Do this with either the invocation option -D or the" - echo "environment variable PGDATA." - ) 1>&2 - exit 1 -fi - - -#------------------------------------------------------------------------- -# Find the input files -#------------------------------------------------------------------------- - -POSTGRES_BKI="$datadir"/postgres.bki -POSTGRES_DESCR="$datadir"/postgres.description - -PG_HBA_SAMPLE="$datadir"/pg_hba.conf.sample -PG_IDENT_SAMPLE="$datadir"/pg_ident.conf.sample -POSTGRESQL_CONF_SAMPLE="$datadir"/postgresql.conf.sample - -if [ "$show_setting" = yes ] || [ "$debug" = yes ] -then - ( - echo - echo "$CMDNAME: internal variables:" - for var in PGDATA datadir PGPATH ENCODING ENCODINGID \ - POSTGRES_SUPERUSERNAME POSTGRES_BKI \ - POSTGRES_DESCR POSTGRESQL_CONF_SAMPLE \ - PG_HBA_SAMPLE PG_IDENT_SAMPLE ; do - eval "echo ' '$var=\$$var" - done - ) 1>&2 -fi - -if [ "$show_setting" = yes ] ; then - exit 0 -fi - -for PREREQ_FILE in "$POSTGRES_BKI" "$POSTGRES_DESCR" \ - "$PG_HBA_SAMPLE" "$PG_IDENT_SAMPLE" "$POSTGRESQL_CONF_SAMPLE" -do - if [ ! -f "$PREREQ_FILE" ] ; then - ( - echo "$CMDNAME: file \"$PREREQ_FILE\" not found" - echo "This means you have a corrupted installation or identified the" - echo "wrong directory with the invocation option -L." - ) 1>&2 - exit 1 - fi -done - -for file in "$POSTGRES_BKI" -do - if [ x"`sed 1q $file`" != x"# PostgreSQL $short_version" ]; then - ( - echo "$CMDNAME: input file \"$file\" does not belong to PostgreSQL $VERSION" - echo "Check your installation or specify the correct path using the option -L." - ) 1>&2 - exit 1 - fi -done - - -trap 'echo "Caught signal." ; exit_nicely' 1 2 3 15 - -# Let's go -echo "The files belonging to this database system will be owned by user \"$EffectiveUser\"." -echo "This user must also own the server process." -echo - -TAB=' ' - -if test x`pg_getlocale CTYPE` = x`pg_getlocale COLLATE` \ - && test x`pg_getlocale CTYPE` = x`pg_getlocale TIME` \ - && test x`pg_getlocale CTYPE` = x`pg_getlocale NUMERIC` \ - && test x`pg_getlocale CTYPE` = x`pg_getlocale MONETARY` \ - && test x`pg_getlocale CTYPE` = x`pg_getlocale MESSAGES` -then - echo "The database cluster will be initialized with locale `pg_getlocale CTYPE`." -else - echo "The database cluster will be initialized with locales:" - echo " COLLATE: `pg_getlocale COLLATE`" - echo " CTYPE: `pg_getlocale CTYPE`" - echo " MESSAGES: `pg_getlocale MESSAGES`" - echo " MONETARY: `pg_getlocale MONETARY`" - echo " NUMERIC: `pg_getlocale NUMERIC`" - echo " TIME: `pg_getlocale TIME`" -fi -echo - - -########################################################################## -# -# CREATE DATABASE DIRECTORY - -# umask must disallow access to group, other for files and dirs -umask 077 - -# find out if directory is empty -pgdata_contents=`ls -A "$PGDATA" 2>/dev/null` -if [ x"$pgdata_contents" != x ] -then - ( - echo "$CMDNAME: directory \"$PGDATA\" exists but is not empty" - echo "If you want to create a new database system, either remove or empty" - echo "the directory \"$PGDATA\" or run $CMDNAME with an argument other than" - echo "\"$PGDATA\"." - ) 1>&2 - exit 1 -else - if [ ! -d "$PGDATA" ]; then - $ECHO_N "creating directory $PGDATA... "$ECHO_C - mkdir -p "$PGDATA" >/dev/null 2>&1 || mkdir "$PGDATA" || exit_nicely - made_new_pgdata=yes - else - $ECHO_N "fixing permissions on existing directory $PGDATA... "$ECHO_C - chmod go-rwx "$PGDATA" || exit_nicely - fi - echo "ok" - - if [ ! -d "$PGDATA"/base ] - then - $ECHO_N "creating directory $PGDATA/base... "$ECHO_C - mkdir "$PGDATA"/base || exit_nicely - echo "ok" - fi - if [ ! -d "$PGDATA"/global ] - then - $ECHO_N "creating directory $PGDATA/global... "$ECHO_C - mkdir "$PGDATA"/global || exit_nicely - echo "ok" - fi - if [ ! -d "$PGDATA"/pg_xlog ] - then - $ECHO_N "creating directory $PGDATA/pg_xlog... "$ECHO_C - mkdir "$PGDATA"/pg_xlog || exit_nicely - echo "ok" - fi - if [ ! -d "$PGDATA"/pg_clog ] - then - $ECHO_N "creating directory $PGDATA/pg_clog... "$ECHO_C - mkdir "$PGDATA"/pg_clog || exit_nicely - echo "ok" - fi -fi - -# Top level PG_VERSION is checked by bootstrapper, so make it first -echo "$short_version" > "$PGDATA/PG_VERSION" || exit_nicely - - -########################################################################## -# -# DETERMINE PLATFORM-SPECIFIC CONFIG SETTINGS -# -# Use reasonable values if kernel will let us, else scale back. Probe for -# max_connections first since it is subject to more constraints than -# shared_buffers. - -# common backend options -PGSQL_OPT="-F -D$PGDATA" - -cp /dev/null "$PGDATA"/postgresql.conf || exit_nicely - -$ECHO_N "selecting default max_connections... "$ECHO_C - -for nconns in 100 50 40 30 20 10 -do - nbuffers=`expr $nconns '*' 5` - TEST_OPT="$PGSQL_OPT -c shared_buffers=$nbuffers -c max_connections=$nconns" - if "$PGPATH"/postgres -boot -x0 $TEST_OPT template1 /dev/null 2>&1 - then - break - fi -done - -echo "$nconns" - -$ECHO_N "selecting default shared_buffers... "$ECHO_C - -for nbuffers in 1000 900 800 700 600 500 400 300 200 100 50 -do - TEST_OPT="$PGSQL_OPT -c shared_buffers=$nbuffers -c max_connections=$nconns" - if "$PGPATH"/postgres -boot -x0 $TEST_OPT template1 /dev/null 2>&1 - then - break - fi -done - -echo "$nbuffers" - -########################################################################## -# -# CREATE CONFIG FILES - -$ECHO_N "creating configuration files... "$ECHO_C - -sed -e "s/^#shared_buffers = 1000/shared_buffers = $nbuffers/" \ - -e "s/^#max_connections = 100/max_connections = $nconns/" \ - -e "s/^#lc_messages = 'C'/lc_messages = '`pg_getlocale MESSAGES`'/" \ - -e "s/^#lc_monetary = 'C'/lc_monetary = '`pg_getlocale MONETARY`'/" \ - -e "s/^#lc_numeric = 'C'/lc_numeric = '`pg_getlocale NUMERIC`'/" \ - -e "s/^#lc_time = 'C'/lc_time = '`pg_getlocale TIME`'/" \ - "$POSTGRESQL_CONF_SAMPLE" > "$PGDATA"/postgresql.conf || exit_nicely -if [ "x$HAVE_IPV6" = xyes ] -then - cp "$PG_HBA_SAMPLE" "$PGDATA"/pg_hba.conf || exit_nicely -else - sed -e "/ ::1 / s/^host/#host/" \ - "$PG_HBA_SAMPLE" > "$PGDATA"/pg_hba.conf || exit_nicely -fi -cp "$PG_IDENT_SAMPLE" "$PGDATA"/pg_ident.conf || exit_nicely - -chmod 0600 "$PGDATA"/pg_hba.conf "$PGDATA"/pg_ident.conf \ - "$PGDATA"/postgresql.conf - -echo "ok" - -########################################################################## -# -# RUN BKI SCRIPT IN BOOTSTRAP MODE TO CREATE TEMPLATE1 - -if [ "$debug" = yes ] -then - BOOTSTRAP_TALK_ARG="-d 5" -fi - -$ECHO_N "creating template1 database in $PGDATA/base/1... "$ECHO_C - -rm -rf "$PGDATA"/base/1 || exit_nicely -mkdir "$PGDATA"/base/1 || exit_nicely - -cat "$POSTGRES_BKI" \ -| sed -e "s/POSTGRES/$POSTGRES_SUPERUSERNAME/g" \ - -e "s/ENCODING/$ENCODINGID/g" \ -| -( - LC_COLLATE=`pg_getlocale COLLATE` - LC_CTYPE=`pg_getlocale CTYPE` - export LC_COLLATE - export LC_CTYPE - unset LC_ALL - "$PGPATH"/postgres -boot -x1 $PGSQL_OPT $BOOTSTRAP_TALK_ARG template1 -) \ -|| exit_nicely - -# Make the per-database PGVERSION for template1 only after init'ing it -echo "$short_version" > "$PGDATA/base/1/PG_VERSION" || exit_nicely - -echo "ok" - -########################################################################## -# -# CREATE VIEWS and other things -# -# NOTE: because here we are driving a standalone backend (not psql), we must -# follow the standalone backend's convention that commands end at a newline. -# To break an SQL command across lines in this script, backslash-escape all -# internal newlines in the command. - -PGSQL_OPT="$PGSQL_OPT -O -c search_path=pg_catalog -c exit_on_error=true" - -$ECHO_N "initializing pg_shadow... "$ECHO_C - -"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null < /dev/null 2>&1 - read FirstPw - stty echo > /dev/null 2>&1 - echo - $ECHO_N "Enter it again: "$ECHO_C - stty -echo > /dev/null 2>&1 - read SecondPw - stty echo > /dev/null 2>&1 - echo - if [ "$FirstPw" != "$SecondPw" ]; then - echo "Passwords didn't match." 1>&2 - exit_nicely - fi - $ECHO_N "setting password... "$ECHO_C - "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <&2 - exit_nicely - fi - echo "ok" -fi - -$ECHO_N "enabling unlimited row size for system tables... "$ECHO_C - -"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null </dev/null </dev/null < 0) AS hastriggers \ - FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ - WHERE C.relkind = 'r'; - -CREATE VIEW pg_indexes AS \ - SELECT \ - N.nspname AS schemaname, \ - C.relname AS tablename, \ - I.relname AS indexname, \ - pg_get_indexdef(I.oid) AS indexdef \ - FROM pg_index X JOIN pg_class C ON (C.oid = X.indrelid) \ - JOIN pg_class I ON (I.oid = X.indexrelid) \ - LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ - WHERE C.relkind = 'r' AND I.relkind = 'i'; - -CREATE VIEW pg_stats AS \ - SELECT \ - nspname AS schemaname, \ - relname AS tablename, \ - attname AS attname, \ - stanullfrac AS null_frac, \ - stawidth AS avg_width, \ - stadistinct AS n_distinct, \ - CASE 1 \ - WHEN stakind1 THEN stavalues1 \ - WHEN stakind2 THEN stavalues2 \ - WHEN stakind3 THEN stavalues3 \ - WHEN stakind4 THEN stavalues4 \ - END AS most_common_vals, \ - CASE 1 \ - WHEN stakind1 THEN stanumbers1 \ - WHEN stakind2 THEN stanumbers2 \ - WHEN stakind3 THEN stanumbers3 \ - WHEN stakind4 THEN stanumbers4 \ - END AS most_common_freqs, \ - CASE 2 \ - WHEN stakind1 THEN stavalues1 \ - WHEN stakind2 THEN stavalues2 \ - WHEN stakind3 THEN stavalues3 \ - WHEN stakind4 THEN stavalues4 \ - END AS histogram_bounds, \ - CASE 3 \ - WHEN stakind1 THEN stanumbers1[1] \ - WHEN stakind2 THEN stanumbers2[1] \ - WHEN stakind3 THEN stanumbers3[1] \ - WHEN stakind4 THEN stanumbers4[1] \ - END AS correlation \ - FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid) \ - JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum) \ - LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) \ - WHERE has_table_privilege(c.oid, 'select'); - -REVOKE ALL on pg_statistic FROM public; - -CREATE VIEW pg_stat_all_tables AS \ - SELECT \ - C.oid AS relid, \ - N.nspname AS schemaname, \ - C.relname AS relname, \ - pg_stat_get_numscans(C.oid) AS seq_scan, \ - pg_stat_get_tuples_returned(C.oid) AS seq_tup_read, \ - sum(pg_stat_get_numscans(I.indexrelid)) AS idx_scan, \ - sum(pg_stat_get_tuples_fetched(I.indexrelid)) AS idx_tup_fetch, \ - pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins, \ - pg_stat_get_tuples_updated(C.oid) AS n_tup_upd, \ - pg_stat_get_tuples_deleted(C.oid) AS n_tup_del \ - FROM pg_class C LEFT JOIN \ - pg_index I ON C.oid = I.indrelid \ - LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ - WHERE C.relkind = 'r' \ - GROUP BY C.oid, N.nspname, C.relname; - -CREATE VIEW pg_stat_sys_tables AS \ - SELECT * FROM pg_stat_all_tables \ - WHERE schemaname IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_stat_user_tables AS \ - SELECT * FROM pg_stat_all_tables \ - WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_statio_all_tables AS \ - SELECT \ - C.oid AS relid, \ - N.nspname AS schemaname, \ - C.relname AS relname, \ - pg_stat_get_blocks_fetched(C.oid) - \ - pg_stat_get_blocks_hit(C.oid) AS heap_blks_read, \ - pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit, \ - sum(pg_stat_get_blocks_fetched(I.indexrelid) - \ - pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_read, \ - sum(pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_hit, \ - pg_stat_get_blocks_fetched(T.oid) - \ - pg_stat_get_blocks_hit(T.oid) AS toast_blks_read, \ - pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit, \ - pg_stat_get_blocks_fetched(X.oid) - \ - pg_stat_get_blocks_hit(X.oid) AS tidx_blks_read, \ - pg_stat_get_blocks_hit(X.oid) AS tidx_blks_hit \ - FROM pg_class C LEFT JOIN \ - pg_index I ON C.oid = I.indrelid LEFT JOIN \ - pg_class T ON C.reltoastrelid = T.oid LEFT JOIN \ - pg_class X ON T.reltoastidxid = X.oid \ - LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ - WHERE C.relkind = 'r' \ - GROUP BY C.oid, N.nspname, C.relname, T.oid, X.oid; - -CREATE VIEW pg_statio_sys_tables AS \ - SELECT * FROM pg_statio_all_tables \ - WHERE schemaname IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_statio_user_tables AS \ - SELECT * FROM pg_statio_all_tables \ - WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_stat_all_indexes AS \ - SELECT \ - C.oid AS relid, \ - I.oid AS indexrelid, \ - N.nspname AS schemaname, \ - C.relname AS relname, \ - I.relname AS indexrelname, \ - pg_stat_get_numscans(I.oid) AS idx_scan, \ - pg_stat_get_tuples_returned(I.oid) AS idx_tup_read, \ - pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch \ - FROM pg_class C JOIN \ - pg_index X ON C.oid = X.indrelid JOIN \ - pg_class I ON I.oid = X.indexrelid \ - LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ - WHERE C.relkind = 'r'; - -CREATE VIEW pg_stat_sys_indexes AS \ - SELECT * FROM pg_stat_all_indexes \ - WHERE schemaname IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_stat_user_indexes AS \ - SELECT * FROM pg_stat_all_indexes \ - WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_statio_all_indexes AS \ - SELECT \ - C.oid AS relid, \ - I.oid AS indexrelid, \ - N.nspname AS schemaname, \ - C.relname AS relname, \ - I.relname AS indexrelname, \ - pg_stat_get_blocks_fetched(I.oid) - \ - pg_stat_get_blocks_hit(I.oid) AS idx_blks_read, \ - pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit \ - FROM pg_class C JOIN \ - pg_index X ON C.oid = X.indrelid JOIN \ - pg_class I ON I.oid = X.indexrelid \ - LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ - WHERE C.relkind = 'r'; - -CREATE VIEW pg_statio_sys_indexes AS \ - SELECT * FROM pg_statio_all_indexes \ - WHERE schemaname IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_statio_user_indexes AS \ - SELECT * FROM pg_statio_all_indexes \ - WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_statio_all_sequences AS \ - SELECT \ - C.oid AS relid, \ - N.nspname AS schemaname, \ - C.relname AS relname, \ - pg_stat_get_blocks_fetched(C.oid) - \ - pg_stat_get_blocks_hit(C.oid) AS blks_read, \ - pg_stat_get_blocks_hit(C.oid) AS blks_hit \ - FROM pg_class C \ - LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ - WHERE C.relkind = 'S'; - -CREATE VIEW pg_statio_sys_sequences AS \ - SELECT * FROM pg_statio_all_sequences \ - WHERE schemaname IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_statio_user_sequences AS \ - SELECT * FROM pg_statio_all_sequences \ - WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); - -CREATE VIEW pg_stat_activity AS \ - SELECT \ - D.oid AS datid, \ - D.datname AS datname, \ - pg_stat_get_backend_pid(S.backendid) AS procpid, \ - pg_stat_get_backend_userid(S.backendid) AS usesysid, \ - U.usename AS usename, \ - pg_stat_get_backend_activity(S.backendid) AS current_query, \ - pg_stat_get_backend_activity_start(S.backendid) AS query_start \ - FROM pg_database D, \ - (SELECT pg_stat_get_backend_idset() AS backendid) AS S, \ - pg_shadow U \ - WHERE pg_stat_get_backend_dbid(S.backendid) = D.oid AND \ - pg_stat_get_backend_userid(S.backendid) = U.usesysid; - -CREATE VIEW pg_stat_database AS \ - SELECT \ - D.oid AS datid, \ - D.datname AS datname, \ - pg_stat_get_db_numbackends(D.oid) AS numbackends, \ - pg_stat_get_db_xact_commit(D.oid) AS xact_commit, \ - pg_stat_get_db_xact_rollback(D.oid) AS xact_rollback, \ - pg_stat_get_db_blocks_fetched(D.oid) - \ - pg_stat_get_db_blocks_hit(D.oid) AS blks_read, \ - pg_stat_get_db_blocks_hit(D.oid) AS blks_hit \ - FROM pg_database D; - -CREATE VIEW pg_locks AS \ - SELECT * \ - FROM pg_lock_status() AS L(relation oid, database oid, \ - transaction xid, pid int4, mode text, granted boolean); - -CREATE VIEW pg_settings AS \ - SELECT * \ - FROM pg_show_all_settings() AS A \ - (name text, setting text, context text, vartype text, \ - source text, min_val text, max_val text); - -CREATE RULE pg_settings_u AS \ - ON UPDATE TO pg_settings \ - WHERE new.name = old.name DO \ - SELECT set_config(old.name, new.setting, 'f'); - -CREATE RULE pg_settings_n AS \ - ON UPDATE TO pg_settings \ - DO INSTEAD NOTHING; - -EOF -if [ "$?" -ne 0 ]; then - exit_nicely -fi -echo "ok" - -$ECHO_N "loading pg_description... "$ECHO_C -( - cat < /dev/null || exit_nicely -echo "ok" - -# Create pg_conversion and support functions -$ECHO_N "creating conversions... "$ECHO_C -grep -v '^DROP CONVERSION' $datadir/conversion_create.sql | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely -echo "ok" - -# Set most system catalogs and built-in functions as world-accessible. -# Some objects may require different permissions by default, so we -# make sure we don't overwrite privilege sets that have already been -# set (NOT NULL). -$ECHO_N "setting privileges on built-in objects... "$ECHO_C -( - cat < /dev/null || exit_nicely -echo "ok" - -$ECHO_N "creating information schema... "$ECHO_C -"$PGPATH"/postgres $PGSQL_OPT -N template1 > /dev/null < "$datadir"/information_schema.sql || exit_nicely -( - # Format version number to format required by information schema (09.08.0007abc). - major_version=`echo $VERSION | sed 's/^\([0-9]*\).*/00\1/;s/.*\(..\)$/\1/'` - minor_version=`echo $VERSION | sed 's/^[0-9]*\.\([0-9]*\).*/00\1/;s/.*\(..\)$/\1/'` - micro_version=`echo $VERSION | sed -e 's/^[0-9]*\.[0-9]*\.\([0-9]*\).*/0000\1/' -e 't L' -e 's/.*/0000/;q' -e ': L' -e 's/.*\(....\)$/\1/'` - letter_version=`echo $VERSION | sed 's/^.*[0-9]\([^0-9]*\)$/\1/'` - combined_version="$major_version.$minor_version.$micro_version$letter_version" - - echo "UPDATE information_schema.sql_implementation_info SET character_value = '$combined_version' WHERE implementation_info_name = 'DBMS VERSION';" - - echo "COPY information_schema.sql_features (feature_id, feature_name, sub_feature_id, sub_feature_name, is_supported, comments) FROM '$datadir/sql_features.txt';" -) \ - | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely -echo "ok" - -$ECHO_N "vacuuming database template1... "$ECHO_C - -"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null </dev/null < 0) AS hastriggers + FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind = 'r'; + +CREATE VIEW pg_indexes AS + SELECT + N.nspname AS schemaname, + C.relname AS tablename, + I.relname AS indexname, + pg_get_indexdef(I.oid) AS indexdef + FROM pg_index X JOIN pg_class C ON (C.oid = X.indrelid) + JOIN pg_class I ON (I.oid = X.indexrelid) + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind = 'r' AND I.relkind = 'i'; + +CREATE VIEW pg_stats AS + SELECT + nspname AS schemaname, + relname AS tablename, + attname AS attname, + stanullfrac AS null_frac, + stawidth AS avg_width, + stadistinct AS n_distinct, + CASE 1 + WHEN stakind1 THEN stavalues1 + WHEN stakind2 THEN stavalues2 + WHEN stakind3 THEN stavalues3 + WHEN stakind4 THEN stavalues4 + END AS most_common_vals, + CASE 1 + WHEN stakind1 THEN stanumbers1 + WHEN stakind2 THEN stanumbers2 + WHEN stakind3 THEN stanumbers3 + WHEN stakind4 THEN stanumbers4 + END AS most_common_freqs, + CASE 2 + WHEN stakind1 THEN stavalues1 + WHEN stakind2 THEN stavalues2 + WHEN stakind3 THEN stavalues3 + WHEN stakind4 THEN stavalues4 + END AS histogram_bounds, + CASE 3 + WHEN stakind1 THEN stanumbers1[1] + WHEN stakind2 THEN stanumbers2[1] + WHEN stakind3 THEN stanumbers3[1] + WHEN stakind4 THEN stanumbers4[1] + END AS correlation + FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid) + JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum) + LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) + WHERE has_table_privilege(c.oid, 'select'); + +REVOKE ALL on pg_statistic FROM public; + +CREATE VIEW pg_stat_all_tables AS + SELECT + C.oid AS relid, + N.nspname AS schemaname, + C.relname AS relname, + pg_stat_get_numscans(C.oid) AS seq_scan, + pg_stat_get_tuples_returned(C.oid) AS seq_tup_read, + sum(pg_stat_get_numscans(I.indexrelid)) AS idx_scan, + sum(pg_stat_get_tuples_fetched(I.indexrelid)) AS idx_tup_fetch, + pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins, + pg_stat_get_tuples_updated(C.oid) AS n_tup_upd, + pg_stat_get_tuples_deleted(C.oid) AS n_tup_del + FROM pg_class C LEFT JOIN + pg_index I ON C.oid = I.indrelid + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind = 'r' + GROUP BY C.oid, N.nspname, C.relname; + +CREATE VIEW pg_stat_sys_tables AS + SELECT * FROM pg_stat_all_tables + WHERE schemaname IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_stat_user_tables AS + SELECT * FROM pg_stat_all_tables + WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_statio_all_tables AS + SELECT + C.oid AS relid, + N.nspname AS schemaname, + C.relname AS relname, + pg_stat_get_blocks_fetched(C.oid) - + pg_stat_get_blocks_hit(C.oid) AS heap_blks_read, + pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit, + sum(pg_stat_get_blocks_fetched(I.indexrelid) - + pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_read, + sum(pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_hit, + pg_stat_get_blocks_fetched(T.oid) - + pg_stat_get_blocks_hit(T.oid) AS toast_blks_read, + pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit, + pg_stat_get_blocks_fetched(X.oid) - + pg_stat_get_blocks_hit(X.oid) AS tidx_blks_read, + pg_stat_get_blocks_hit(X.oid) AS tidx_blks_hit + FROM pg_class C LEFT JOIN + pg_index I ON C.oid = I.indrelid LEFT JOIN + pg_class T ON C.reltoastrelid = T.oid LEFT JOIN + pg_class X ON T.reltoastidxid = X.oid + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind = 'r' + GROUP BY C.oid, N.nspname, C.relname, T.oid, X.oid; + +CREATE VIEW pg_statio_sys_tables AS + SELECT * FROM pg_statio_all_tables + WHERE schemaname IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_statio_user_tables AS + SELECT * FROM pg_statio_all_tables + WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_stat_all_indexes AS + SELECT + C.oid AS relid, + I.oid AS indexrelid, + N.nspname AS schemaname, + C.relname AS relname, + I.relname AS indexrelname, + pg_stat_get_numscans(I.oid) AS idx_scan, + pg_stat_get_tuples_returned(I.oid) AS idx_tup_read, + pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch + FROM pg_class C JOIN + pg_index X ON C.oid = X.indrelid JOIN + pg_class I ON I.oid = X.indexrelid + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind = 'r'; + +CREATE VIEW pg_stat_sys_indexes AS + SELECT * FROM pg_stat_all_indexes + WHERE schemaname IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_stat_user_indexes AS + SELECT * FROM pg_stat_all_indexes + WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_statio_all_indexes AS + SELECT + C.oid AS relid, + I.oid AS indexrelid, + N.nspname AS schemaname, + C.relname AS relname, + I.relname AS indexrelname, + pg_stat_get_blocks_fetched(I.oid) - + pg_stat_get_blocks_hit(I.oid) AS idx_blks_read, + pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit + FROM pg_class C JOIN + pg_index X ON C.oid = X.indrelid JOIN + pg_class I ON I.oid = X.indexrelid + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind = 'r'; + +CREATE VIEW pg_statio_sys_indexes AS + SELECT * FROM pg_statio_all_indexes + WHERE schemaname IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_statio_user_indexes AS + SELECT * FROM pg_statio_all_indexes + WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_statio_all_sequences AS + SELECT + C.oid AS relid, + N.nspname AS schemaname, + C.relname AS relname, + pg_stat_get_blocks_fetched(C.oid) - + pg_stat_get_blocks_hit(C.oid) AS blks_read, + pg_stat_get_blocks_hit(C.oid) AS blks_hit + FROM pg_class C + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE C.relkind = 'S'; + +CREATE VIEW pg_statio_sys_sequences AS + SELECT * FROM pg_statio_all_sequences + WHERE schemaname IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_statio_user_sequences AS + SELECT * FROM pg_statio_all_sequences + WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); + +CREATE VIEW pg_stat_activity AS + SELECT + D.oid AS datid, + D.datname AS datname, + pg_stat_get_backend_pid(S.backendid) AS procpid, + pg_stat_get_backend_userid(S.backendid) AS usesysid, + U.usename AS usename, + pg_stat_get_backend_activity(S.backendid) AS current_query, + pg_stat_get_backend_activity_start(S.backendid) AS query_start + FROM pg_database D, + (SELECT pg_stat_get_backend_idset() AS backendid) AS S, + pg_shadow U + WHERE pg_stat_get_backend_dbid(S.backendid) = D.oid AND + pg_stat_get_backend_userid(S.backendid) = U.usesysid; + +CREATE VIEW pg_stat_database AS + SELECT + D.oid AS datid, + D.datname AS datname, + pg_stat_get_db_numbackends(D.oid) AS numbackends, + pg_stat_get_db_xact_commit(D.oid) AS xact_commit, + pg_stat_get_db_xact_rollback(D.oid) AS xact_rollback, + pg_stat_get_db_blocks_fetched(D.oid) - + pg_stat_get_db_blocks_hit(D.oid) AS blks_read, + pg_stat_get_db_blocks_hit(D.oid) AS blks_hit + FROM pg_database D; + +CREATE VIEW pg_locks AS + SELECT * + FROM pg_lock_status() AS L(relation oid, database oid, + transaction xid, pid int4, mode text, granted boolean); + +CREATE VIEW pg_settings AS + SELECT * + FROM pg_show_all_settings() AS A + (name text, setting text, context text, vartype text, + source text, min_val text, max_val text); + +CREATE RULE pg_settings_u AS + ON UPDATE TO pg_settings + WHERE new.name = old.name DO + SELECT set_config(old.name, new.setting, 'f'); + +CREATE RULE pg_settings_n AS + ON UPDATE TO pg_settings + DO INSTEAD NOTHING; + +