Commit 279598bb authored by Bruce Momjian's avatar Bruce Momjian

Add C version of initdb, from Andrew Dunstan.

This is his original version with a binary rmdir() we might need in the
future.

I will commit an update version with cleanups shortly.
parent 5e8a8865
......@@ -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)
/*-------------------------------------------------------------------------
*
* 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 <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <locale.h>
#include <signal.h>
#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 <pgsql-bugs@postgresql.org>.\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;
}
#!@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 <pgsql-bugs@postgresql.org>."
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 >/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 >/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 <<EOF
-- 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();
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();
-- needs to be done before alter user, because alter user checks that
-- pg_shadow is secure ...
REVOKE ALL on pg_shadow FROM public;
EOF
if [ "$?" -ne 0 ]; then
exit_nicely
fi
echo "ok"
# set up password
if [ "$PwPrompt" ]; then
$ECHO_N "Enter new superuser password: "$ECHO_C
stty -echo > /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 <<EOF
ALTER USER "$POSTGRES_SUPERUSERNAME" WITH PASSWORD '$FirstPw';
EOF
if [ "$?" -ne 0 ]; then
exit_nicely
fi
if [ ! -f "$PGDATA"/global/pg_pwd ]; then
echo
echo "$CMDNAME: The password file wasn't generated. Please report this problem." 1>&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 <<EOF
ALTER TABLE pg_attrdef CREATE TOAST TABLE;
ALTER TABLE pg_constraint CREATE TOAST TABLE;
ALTER TABLE pg_database CREATE TOAST TABLE;
ALTER TABLE pg_description CREATE TOAST TABLE;
ALTER TABLE pg_group CREATE TOAST TABLE;
ALTER TABLE pg_proc CREATE TOAST TABLE;
ALTER TABLE pg_rewrite CREATE TOAST TABLE;
ALTER TABLE pg_shadow CREATE TOAST TABLE;
ALTER TABLE pg_statistic CREATE TOAST TABLE;
EOF
if [ "$?" -ne 0 ]; then
exit_nicely
fi
echo "ok"
$ECHO_N "initializing pg_depend... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
-- 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;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_class;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_proc;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_type;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_cast;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_constraint;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_attrdef;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_language;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_operator;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_opclass;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_rewrite;
INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_trigger;
-- 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%';
EOF
if [ "$?" -ne 0 ]; then
exit_nicely
fi
echo "ok"
$ECHO_N "creating system views... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
CREATE VIEW pg_user AS \
SELECT \
usename, \
usesysid, \
usecreatedb, \
usesuper, \
usecatupd, \
'********'::text as passwd, \
valuntil, \
useconfig \
FROM pg_shadow;
CREATE VIEW pg_rules AS \
SELECT \
N.nspname AS schemaname, \
C.relname AS tablename, \
R.rulename AS rulename, \
pg_get_ruledef(R.oid) AS definition \
FROM (pg_rewrite R JOIN pg_class C ON (C.oid = R.ev_class)) \
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \
WHERE R.rulename != '_RETURN';
CREATE VIEW pg_views AS \
SELECT \
N.nspname AS schemaname, \
C.relname AS viewname, \
pg_get_userbyid(C.relowner) AS viewowner, \
pg_get_viewdef(C.oid) AS definition \
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \
WHERE C.relkind = 'v';
CREATE VIEW pg_tables AS \
SELECT \
N.nspname AS schemaname, \
C.relname AS tablename, \
pg_get_userbyid(C.relowner) AS tableowner, \
C.relhasindex AS hasindexes, \
C.relhasrules AS hasrules, \
(C.reltriggers > 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 <<EOF
CREATE TEMP TABLE tmp_pg_description ( \
objoid oid, \
classname name, \
objsubid int4, \
description text) WITHOUT OIDS;
COPY tmp_pg_description FROM STDIN;
EOF
cat "$POSTGRES_DESCR"
cat <<EOF
\.
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;
EOF
) \
| "$PGPATH"/postgres $PGSQL_OPT template1 > /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 <<EOF
UPDATE pg_class SET relacl = '{"=r/\\\\"$POSTGRES_SUPERUSERNAME\\\\""}' \
WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;
UPDATE pg_proc SET proacl = '{"=X/\\\\"$POSTGRES_SUPERUSERNAME\\\\""}' \
WHERE proacl IS NULL;
UPDATE pg_language SET lanacl = '{"=U/\\\\"$POSTGRES_SUPERUSERNAME\\\\""}' \
WHERE lanpltrusted;
GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;
GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;
EOF
) \
| "$PGPATH"/postgres $PGSQL_OPT template1 > /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 <<EOF
ANALYZE;
VACUUM FULL FREEZE;
EOF
if [ "$?" -ne 0 ]; then
exit_nicely
fi
echo "ok"
$ECHO_N "copying template1 to template0... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
CREATE DATABASE template0;
UPDATE pg_database SET \
datistemplate = 't', \
datallowconn = 'f' \
WHERE datname = 'template0';
-- We use the OID of template0 to determine lastsysoid
UPDATE pg_database SET datlastsysoid = \
(SELECT oid::int4 - 1 FROM pg_database WHERE datname = 'template0');
-- 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;
REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;
-- Finally vacuum to clean up dead rows in pg_database
VACUUM FULL pg_database;
EOF
if [ "$?" -ne 0 ]; then
exit_nicely
fi
echo "ok"
##########################################################################
#
# FINISHED
echo
echo "Success. You can now start the database server using:"
echo ""
echo " $PGPATH/postmaster -D $PGDATA"
echo "or"
# (Advertise -l option here, otherwise we have a background
# process writing to the terminal.)
echo " $PGPATH/pg_ctl -D $PGDATA -l logfile start"
echo
exit 0
CREATE VIEW pg_user AS
SELECT
usename,
usesysid,
usecreatedb,
usesuper,
usecatupd,
'********'::text as passwd,
valuntil,
useconfig
FROM pg_shadow;
CREATE VIEW pg_rules AS
SELECT
N.nspname AS schemaname,
C.relname AS tablename,
R.rulename AS rulename,
pg_get_ruledef(R.oid) AS definition
FROM (pg_rewrite R JOIN pg_class C ON (C.oid = R.ev_class))
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE R.rulename != '_RETURN';
CREATE VIEW pg_views AS
SELECT
N.nspname AS schemaname,
C.relname AS viewname,
pg_get_userbyid(C.relowner) AS viewowner,
pg_get_viewdef(C.oid) AS definition
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind = 'v';
CREATE VIEW pg_tables AS
SELECT
N.nspname AS schemaname,
C.relname AS tablename,
pg_get_userbyid(C.relowner) AS tableowner,
C.relhasindex AS hasindexes,
C.relhasrules AS hasrules,
(C.reltriggers > 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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment