--- /dev/null +++ xtra/admin/admin.sql.in @@ -0,0 +1,63 @@ +/* ********************************** + * Administrative functions + * ********************************* */ + +/* generic file access functions (genfile.c) */ + +CREATE FUNCTION pg_file_stat(text) RETURNS record + AS 'MODULE_PATHNAME', 'pg_file_stat' + LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION pg_file_length(text) RETURNS bigint + AS 'SELECT len FROM pg_file_stat($1) AS s(len int8, c timestamp, a timestamp, m timestamp, i bool)' + LANGUAGE SQL VOLATILE STRICT; + +CREATE FUNCTION pg_file_read(text, bigint, bigint) RETURNS text + AS 'MODULE_PATHNAME', 'pg_file_read' + LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION pg_file_write(text, text, bool) RETURNS bigint + AS 'MODULE_PATHNAME', 'pg_file_write' + LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION pg_file_rename(text, text, text) RETURNS bool + AS 'MODULE_PATHNAME', 'pg_file_rename' + LANGUAGE C VOLATILE; + +CREATE FUNCTION pg_file_unlink(text) RETURNS bool + AS 'MODULE_PATHNAME', 'pg_file_unlink' + LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION pg_file_rename(text, text) RETURNS bool + AS 'SELECT pg_file_rename($1, $2, NULL); ' + LANGUAGE SQL VOLATILE STRICT; + +CREATE FUNCTION pg_dir_ls(text, bool) RETURNS setof text + AS 'MODULE_PATHNAME', 'pg_dir_ls' + LANGUAGE C VOLATILE STRICT; + + +/* Miscellaneous functions (misc.c) */ + +CREATE FUNCTION pg_reload_conf() RETURNS int4 + AS 'MODULE_PATHNAME', 'pg_reload_conf' + LANGUAGE C STABLE STRICT; + +/* +CREATE FUNCTION pg_logfile_rotate() RETURNS bool + AS 'MODULE_PATHNAME', 'pg_logfile_rotate' + LANGUAGE C STABLE STRICT; +*/ + +CREATE FUNCTION pg_postmaster_starttime() RETURNS timestamp + AS 'MODULE_PATHNAME', 'pg_postmaster_starttime' + LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION pg_logdir_ls() RETURNS setof record + AS 'MODULE_PATHNAME', 'pg_logdir_ls' + LANGUAGE C VOLATILE STRICT; + +CREATE VIEW pg_logdir_ls AS + SELECT * + FROM pg_logdir_ls() AS A + (filetime timestamp, filename text); --- /dev/null +++ xtra/admin/genfile.c @@ -0,0 +1,476 @@ +/*------------------------------------------------------------------------- + * + * genfile.c + * + * + * Copyright (c) 2004 - 2005, PostgreSQL Global Development Group + * + * Author: Andreas Pflug + * + * IDENTIFICATION + * $PostgreSQL: $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include +#include +#include +#include + +#include "miscadmin.h" +#include "storage/fd.h" +#include "catalog/pg_type.h" +#include "funcapi.h" + + +#ifdef WIN32 + +#ifdef rename +#undef rename +#endif + +#ifdef unlink +#undef unlink +#endif + +#endif + +extern DLLIMPORT char *DataDir; +extern DLLIMPORT char *Log_directory; +extern DLLIMPORT char *Log_filename_prefix; + + +Datum pg_file_stat(PG_FUNCTION_ARGS); +Datum pg_file_read(PG_FUNCTION_ARGS); +Datum pg_file_write(PG_FUNCTION_ARGS); +Datum pg_file_rename(PG_FUNCTION_ARGS); +Datum pg_file_unlink(PG_FUNCTION_ARGS); +Datum pg_dir_ls(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(pg_file_stat); +PG_FUNCTION_INFO_V1(pg_file_read); +PG_FUNCTION_INFO_V1(pg_file_write); +PG_FUNCTION_INFO_V1(pg_file_rename); +PG_FUNCTION_INFO_V1(pg_file_unlink); +PG_FUNCTION_INFO_V1(pg_dir_ls); + +typedef struct +{ + char *location; + DIR *dirdesc; +} directory_fctx; + +/*----------------------- + * some helper functions + */ + +/* + * Return an absolute path. Argument may be absolute or + * relative to the DataDir. + */ +static char *absClusterPath(text *arg, bool logAllowed) +{ + char *filename; + int len=VARSIZE(arg) - VARHDRSZ; + int dlen = strlen(DataDir); + + filename = palloc(len+1); + memcpy(filename, VARDATA(arg), len); + filename[len] = 0; + + if (strstr(filename, "..") != NULL) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("No .. allowed in filenames")))); + + if (is_absolute_path(filename)) + { + if (logAllowed && !strncmp(filename, Log_directory, strlen(Log_directory))) + return filename; + if (strncmp(filename, DataDir, dlen)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("Absolute path not allowed")))); + + return filename; + } + else + { + char *absname = palloc(dlen+len+2); + sprintf(absname, "%s/%s", DataDir, filename); + pfree(filename); + return absname; + } +} + + +/* + * check for superuser, bark if not. + */ +static void +requireSuperuser(void) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser may access generic file functions")))); +} + + + +/* ------------------------------------ + * generic file handling functions + */ + + +Datum pg_file_stat(PG_FUNCTION_ARGS) +{ + AttInMetadata *attinmeta = NULL; + char * filename = absClusterPath(PG_GETARG_TEXT_P(0), true); + struct stat fst; + int64 length; + char lenbuf[30]; + char cbuf[30], abuf[30], mbuf[30], dbuf[]="f"; + char *values[5] = + { lenbuf, cbuf, abuf, mbuf, dbuf }; + + pg_time_t timestamp; + HeapTuple tuple; + + if (attinmeta == NULL) + { + TupleDesc tupdesc = CreateTemplateTupleDesc(5, false); + + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "length", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ctime", + TIMESTAMPOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "atime", + TIMESTAMPOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "mtime", + TIMESTAMPOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "isdir", + BOOLOID, -1, 0); + + attinmeta = TupleDescGetAttInMetadata(tupdesc); + } + + if (stat(filename, &fst) < 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("could not stat file %s: %m", filename))); + + strcpy(lenbuf, "-1"); + strcpy(cbuf, "NULL"); + strcpy(abuf, "NULL"); + strcpy(mbuf, "NULL"); + } + else + { + length = fst.st_size; + snprintf(lenbuf, 30, INT64_FORMAT, length); + + timestamp = fst.st_ctime; + pg_strftime(cbuf, 30, "%F %T", pg_localtime(×tamp)); + + timestamp = fst.st_atime; + pg_strftime(abuf, 30, "%F %T", pg_localtime(×tamp)); + + timestamp = fst.st_mtime; + pg_strftime(mbuf, 30, "%F %T", pg_localtime(×tamp)); + + if (fst.st_mode & S_IFDIR) + dbuf[0] = 't'; + } + tuple = BuildTupleFromCStrings(attinmeta, values); + + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); +} + + +Datum pg_file_read(PG_FUNCTION_ARGS) +{ + size_t size; + char *buf=0; + size_t nbytes; + int64 pos; + FILE *f; + char *filename; + + requireSuperuser(); + + filename = absClusterPath(PG_GETARG_TEXT_P(0), true); + pos = PG_GETARG_INT64(1); + size = PG_GETARG_INT64(2); + + f = fopen(filename, "rb"); + if (!f) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open file %s for reading: %m", filename))); + PG_RETURN_NULL(); + } + + if (pos >= 0) + fseek(f, pos, SEEK_SET); + else + fseek(f, pos, SEEK_END); + + + buf = palloc(size + VARHDRSZ); + + nbytes = fread(VARDATA(buf), 1, size, f); + if (nbytes < 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read file %s: %m", filename))); + PG_RETURN_NULL(); + } + VARATT_SIZEP(buf) = nbytes + VARHDRSZ; + fclose(f); + + PG_RETURN_TEXT_P(buf); +} + + +Datum pg_file_write(PG_FUNCTION_ARGS) +{ + FILE *f; + char *filename; + text *data; + int64 count = 0; + + requireSuperuser(); + + filename = absClusterPath(PG_GETARG_TEXT_P(0), false); + data = PG_GETARG_TEXT_P(1); + + if (PG_ARGISNULL(2) || !PG_GETARG_BOOL(2)) + { + struct stat fst; + if (stat(filename, &fst) >= 0) + ereport(ERROR, + (ERRCODE_DUPLICATE_FILE, + errmsg("file %s exists", filename))); + + f = fopen(filename, "wb"); + } + else + f = fopen(filename, "ab"); + + if (!f) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could open file %s for writing: %m", filename))); + } + + if (VARSIZE(data) != 0) + { + count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f); + + if (count != VARSIZE(data) - VARHDRSZ) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("error writing file %s: %m", filename))); + } + fclose(f); + + PG_RETURN_INT64(count); +} + + +Datum pg_file_rename(PG_FUNCTION_ARGS) +{ + char *fn1, *fn2, *fn3; + int rc; + + requireSuperuser(); + + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + PG_RETURN_NULL(); + + fn1=absClusterPath(PG_GETARG_TEXT_P(0), false); + fn2=absClusterPath(PG_GETARG_TEXT_P(1), false); + if (PG_ARGISNULL(2)) + fn3=0; + else + fn3=absClusterPath(PG_GETARG_TEXT_P(2), false); + + if (access(fn1, W_OK) < 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("file %s not accessible: %m", fn1))); + + PG_RETURN_BOOL(false); + } + + if (fn3 && access(fn2, W_OK) < 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("file %s not accessible: %m", fn2))); + + PG_RETURN_BOOL(false); + } + + + rc = access(fn3 ? fn3 : fn2, 2); + if (rc >= 0 || errno != ENOENT) + { + ereport(ERROR, + (ERRCODE_DUPLICATE_FILE, + errmsg("cannot rename to target file %s", fn3 ? fn3 : fn2))); + } + + if (fn3) + { + if (rename(fn2, fn3) != 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not rename %s to %s: %m", fn2, fn3))); + } + if (rename(fn1, fn2) != 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("could not rename %s to %s: %m", fn1, fn2))); + + if (rename(fn3, fn2) != 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not rename %s back to %s: %m", fn3, fn2))); + } + else + { + ereport(ERROR, + (ERRCODE_UNDEFINED_FILE, + errmsg("renaming %s to %s was reverted", fn2, fn3))); + + } + } + } + else if (rename(fn1, fn2) != 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("renaming %s to %s %m", fn1, fn2))); + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not rename %s to %s: %m", fn1, fn2))); + } + + PG_RETURN_BOOL(true); +} + + +Datum pg_file_unlink(PG_FUNCTION_ARGS) +{ + char *filename; + + requireSuperuser(); + + filename = absClusterPath(PG_GETARG_TEXT_P(0), false); + + if (access(filename, W_OK) < 0) + { + if (errno == ENOENT) + PG_RETURN_BOOL(false); + else + ereport(ERROR, + (errcode_for_file_access(), + errmsg("file %s not accessible: %m", filename))); + + } + + if (unlink(filename) < 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("could not unlink file %s: %m", filename))); + + PG_RETURN_BOOL(false); + } + PG_RETURN_BOOL(true); +} + + +Datum pg_dir_ls(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + struct dirent *de; + directory_fctx *fctx; + + requireSuperuser(); + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + + funcctx=SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(directory_fctx)); + fctx->location = absClusterPath(PG_GETARG_TEXT_P(0), false); + + fctx->dirdesc = AllocateDir(fctx->location); + + if (!fctx->dirdesc) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("%s is not browsable: %m", fctx->location))); + + if (PG_ARGISNULL(1) || !PG_GETARG_BOOL(1)) + { + pfree(fctx->location); + fctx->location = 0; + } + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + funcctx=SRF_PERCALL_SETUP(); + fctx = (directory_fctx*) funcctx->user_fctx; + + if (!fctx->dirdesc) /* not a readable directory */ + SRF_RETURN_DONE(funcctx); + + while ((de = readdir(fctx->dirdesc)) != NULL) + { + char *name; + text *result; + int len; + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (fctx->location) + { + char *path=palloc(strlen(fctx->location) + strlen(de->d_name) +2); + sprintf(path, "%s/%s", fctx->location, de->d_name); + + name = path; + } + else + name = de->d_name; + + + len = strlen(name); + result = palloc(len + VARHDRSZ); + VARATT_SIZEP(result) = len + VARHDRSZ; + memcpy(VARDATA(result), name, len); + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(result)); + } + + FreeDir(fctx->dirdesc); + SRF_RETURN_DONE(funcctx); +} + + --- /dev/null +++ xtra/admin/Makefile @@ -0,0 +1,11 @@ +subdir = contrib/admin +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global + +MODULE_big = admin +PG_CPPFLAGS = -I$(libpq_srcdir) +DATA_built = admin.sql +DOCS = README.admin +OBJS = genfile.o misc.o + +include $(top_srcdir)/contrib/contrib-global.mk --- /dev/null +++ xtra/admin/misc.c @@ -0,0 +1,303 @@ +/* + * admin.c + * miscellaneous administrative functions + * + * Copyright (c) 2004 - 2005, PostgreSQL Global Development Group + * + * Author: Andreas Pflug + * + * IDENTIFICATION + * $PostgreSQL: $ + * + */ + +#include "postgres.h" + +#include +#include +#include +#include +#include + +#include "commands/dbcommands.h" +#include "miscadmin.h" +#include "storage/sinval.h" +#include "storage/fd.h" +#include "funcapi.h" +#include "catalog/pg_type.h" +#include "catalog/pg_tablespace.h" +#include "postmaster/syslogger.h" + +extern DLLIMPORT char *DataDir; +extern DLLIMPORT char *Log_directory; +extern DLLIMPORT char *Log_filename; +extern DLLIMPORT pid_t PostmasterPid; + +Datum pg_reload_conf(PG_FUNCTION_ARGS); +Datum pg_logfile_rotate(PG_FUNCTION_ARGS); +Datum pg_logdir_ls(PG_FUNCTION_ARGS); +Datum pg_postmaster_starttime(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(pg_reload_conf); +PG_FUNCTION_INFO_V1(pg_logfile_rotate); +PG_FUNCTION_INFO_V1(pg_logdir_ls); +PG_FUNCTION_INFO_V1(pg_postmaster_starttime); +Datum +pg_reload_conf(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser can signal the postmaster")))); + + if (kill(PostmasterPid, SIGHUP)) + { + ereport(WARNING, + (errmsg("failed to send signal to postmaster: %m"))); + + PG_RETURN_INT32(0); + } + + PG_RETURN_INT32(1); +} + + +typedef struct +{ + char *location; + DIR *dirdesc; +} directory_fctx; + + +/* + * logfile handling functions + */ + +#if ROTATE_SUPPORTED +/* not yet in backend */ +Datum pg_logfile_rotate(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser can issue a logfile rotation command")))); + + PG_RETURN_BOOL(LogFileRotate()); +} + +#endif + +Datum pg_logdir_ls(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + struct dirent *de; + directory_fctx *fctx; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser can list the log directory")))); + + if (memcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log", 30) != 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'")))); + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc tupdesc; + + funcctx=SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(directory_fctx)); + if (is_absolute_path(Log_directory)) + fctx->location = Log_directory; + else + { + fctx->location = palloc(strlen(DataDir) + strlen(Log_directory) +2); + sprintf(fctx->location, "%s/%s", DataDir, Log_directory); + } + tupdesc = CreateTemplateTupleDesc(2, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", + TIMESTAMPOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", + TEXTOID, -1, 0); + + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + fctx->dirdesc = AllocateDir(fctx->location); + + if (!fctx->dirdesc) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("%s is not browsable: %m", fctx->location))); + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + funcctx=SRF_PERCALL_SETUP(); + fctx = (directory_fctx*) funcctx->user_fctx; + + if (!fctx->dirdesc) /* not a readable directory */ + SRF_RETURN_DONE(funcctx); + + while ((de = readdir(fctx->dirdesc)) != NULL) + { + char *values[2]; + HeapTuple tuple; + + char *field[MAXDATEFIELDS]; + char lowstr[MAXDATELEN + 1]; + int dtype; + int nf, ftype[MAXDATEFIELDS]; + fsec_t fsec; + int tz = 0; + struct pg_tm date; + + /* + * Default format: + * postgresql-YYYY-MM-DD_HHMMSS.log + */ + if (strlen(de->d_name) != 32 + || memcmp(de->d_name, "postgresql-", 11) + || de->d_name[21] != '_' + || strcmp(de->d_name + 28, ".log")) + continue; + + values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2); + sprintf(values[1], "%s/%s", fctx->location, de->d_name); + + values[0] = de->d_name + 11; /* timestamp */ + values[0][17] = 0; + + /* parse and decode expected timestamp */ + if (ParseDateTime(values[0], lowstr, field, ftype, MAXDATEFIELDS, &nf)) + continue; + + if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz)) + continue; + + /* Seems the format fits the expected format; feed it into the tuple */ + + tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } + + FreeDir(fctx->dirdesc); + SRF_RETURN_DONE(funcctx); +} + + +Datum pg_postmaster_starttime(PG_FUNCTION_ARGS) +{ + Timestamp result; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser can query the postmaster starttime")))); + +#ifdef WIN32 + { + struct pg_tm tm; + extern DLLIMPORT HANDLE PostmasterHandle; + FILETIME creationTime; + FILETIME exitTime, kernelTime, userTime; + SYSTEMTIME st; + + bool rc=GetProcessTimes(PostmasterHandle, &creationTime, + &exitTime, &kernelTime, &userTime); + if (!rc) + PG_RETURN_NULL(); + + FileTimeToSystemTime(&creationTime, &st); + + tm.tm_year = st.wYear; + tm.tm_mon = st.wMonth; + tm.tm_mday = st.wDay; + tm.tm_hour = st.wHour; + tm.tm_min = st.wMinute; + tm.tm_sec = st.wSecond; + + if (tm2timestamp(&tm, 0, NULL, &result) != 0) + { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("postmaster timestamp out of range"))); + } + } +#else + { + FILE *fp; + char buffer[MAXPGPATH]; + pg_time_t now = time(NULL); + struct pg_tm *tm = pg_localtime(&now); + double systemSeconds; + long procJiffies; + int i; + + tm->tm_year += 1900; + tm->tm_mon += 1; + + if (tm2timestamp(tm, 0, NULL, &result) != 0) + { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("postmaster timestamp out of range"))); + } + + fp = fopen("/proc/uptime", "r"); + if (!fp) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open /proc/uptime: %m"))); + + if (fscanf(fp, "%lf", &systemSeconds) != 1) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not interpret /proc/uptime: %m"))); + + fclose(fp); + + sprintf(buffer, "/proc/%u/stat", PostmasterPid); + fp = fopen(buffer, "r"); + if (!fp) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open %s: %m", buffer))); + + +#define PROC_STAT_POS 22 + + for (i=1 ; i < PROC_STAT_POS ; i++) + { + char c; + do + { + c = fgetc(fp); + } + while (c && c != ' '); + } + + if (fscanf(fp, "%ld", &procJiffies) != 1) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not interpret %s: %m", buffer))); + + fclose(fp); + +#ifdef HAVE_INT64_TIMESTAMP + result += (procJiffies/100. - systemSeconds) / INT64CONST(1000000); +#else + result += (procJiffies/100. - systemSeconds); +#endif + } +#endif + + + PG_RETURN_TIMESTAMP(result); +} --- /dev/null +++ xtra/admin/README.admin @@ -0,0 +1,48 @@ +PostgreSQL Administration Functions +=================================== + +This directory is a PostgreSQL 'contrib' module which implements a number of +support functions which pgAdmin will use to provide additional functionality +if installed on a server. + +Installation +============ + +You will need a PostgreSQL source tree which has been properly configured +for the local system using the 'configure' script. + +Copy this directory into the /contrib directory of the PostgreSQL source tree, +and then run the following commands as a users with appropriate privileges: + +make +make install + +pgAdmin will look for the functions in the Initial Database specified in the +connection dialogue for the server. To install the functions in the database, +either run the admin.sql script using the pgAdmin SQL tool (and then restart +pgAdmin), or run the script using psql, eg: + +psql -u postgres template1 < admin.sql + +Objects implemented +=================== + +genfile.c (superuser only): +----------------------------- +record pg_file_stat(fname text) +int8 pg_file_length(fname text) +text pg_file_read(fname text, offs int8, len int8) +int8 pg_file_write(fname text, data text, append bool) +bool pg_file_rename(oldname text, newname text) +bool pg_file_rename(oldname text, newname text, archivname text) +bool pg_file_unlink(fname text) +setof text pg_dir_ls(dirname text, bool fullpath) + + +misc.c (superuser only): +----------------------------- +int4 pg_reload_conf() +bool pg_logfile_rotate() +setof record pg_logdir_ls() + +VIEW pg_logdir_ls(filetime timestamp, filename text)