Re: logfile subprocess and Fancy File Functions - Mailing list pgsql-patches

From Andreas Pflug
Subject Re: logfile subprocess and Fancy File Functions
Date
Msg-id 40FE3F93.2060308@pse-consulting.de
Whole thread Raw
In response to Re: logfile subprocess and Fancy File Functions  (Bruce Momjian <pgman@candle.pha.pa.us>)
Responses Re: logfile subprocess and Fancy File Functions  (Bruce Momjian <pgman@candle.pha.pa.us>)
List pgsql-patches
Bruce Momjian wrote:
> Andreas Pflug wrote:
>

>
>
> OK, new idea.  Forget about modifying pg_dir_ls().  Instead add
> pg_file_stat the returns the file size, times.  You can then easily use
> that for file size and times.  Also, if you want, add an is_dir boolean
> so people can write functions that walk the directory tree.

I now replaced pg_logfile_length, instead pg_logfile_stat(text) will
return a record (len int8, ctime timestamp, atime timestamp, mtime
timestamp, isdir bool).

For convenience, I'd like to have the function

CREATE FUNCTION pg_file_length(text) RETURNS int8
AS
$BODY$
SELECT len
   FROM pg_file_stat($1) AS stat
    (len int8, ctime timestamp,
    atime timestamp, mtime timestamp, isdir bool)
$BODY$ LANGUAGE SQL STRICT;

Where is the right place to put it?

Also, I wonder how to join pg_file_stat and pg_dir_ls to get a ls -l
like listing. Apparently I can't do that, unless I don't code pg_dir_ls
as returning records too, right?


>
> I noticed we had a big logging discussion during 7.4 beta about logging
> and log rotation.  This patch is clearly superior to the ideas we had at
> that time.
>

Currently, the discussion circles around file functions, not logging. If
you think that part is clean, how about committing it separately so it
can be tested/used (no problem if pg_logfile_rotate() isn't available
right from the start). I'll supply docs RSN.

Regards,
Andreas
Index: src/backend/catalog/system_views.sql
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/catalog/system_views.sql,v
retrieving revision 1.6
diff -u -r1.6 system_views.sql
--- src/backend/catalog/system_views.sql    26 Apr 2004 15:24:41 -0000    1.6
+++ src/backend/catalog/system_views.sql    21 Jul 2004 09:49:22 -0000
@@ -273,3 +273,8 @@
     DO INSTEAD NOTHING;

 GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+
+CREATE VIEW pg_logdir_ls AS
+    SELECT *
+    FROM pg_logdir_ls() AS A
+    (filetime timestamp, pid int4, filename text);
Index: src/backend/postmaster/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v
retrieving revision 1.16
diff -u -r1.16 Makefile
--- src/backend/postmaster/Makefile    19 Jul 2004 02:47:08 -0000    1.16
+++ src/backend/postmaster/Makefile    21 Jul 2004 09:49:23 -0000
@@ -12,7 +12,7 @@
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global

-OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
+OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o syslogger.o

 all: SUBSYS.o

Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.412
diff -u -r1.412 postmaster.c
--- src/backend/postmaster/postmaster.c    19 Jul 2004 02:47:08 -0000    1.412
+++ src/backend/postmaster/postmaster.c    21 Jul 2004 09:49:29 -0000
@@ -118,7 +118,7 @@
 #include "utils/ps_status.h"
 #include "bootstrap/bootstrap.h"
 #include "pgstat.h"
-
+#include "postmaster/syslogger.h"

 /*
  * List of active backends (or child processes anyway; we don't actually
@@ -201,6 +201,7 @@
             BgWriterPID = 0,
             PgArchPID = 0,
             PgStatPID = 0;
+pid_t       SysLoggerPID = 0;

 /* Startup/shutdown state */
 #define            NoShutdown        0
@@ -852,6 +853,12 @@
 #endif

     /*
+     * start logging to file
+     */
+
+    SysLoggerPID = SysLogger_Start();
+
+    /*
      * Reset whereToSendOutput from Debug (its starting state) to None.
      * This prevents ereport from sending log messages to stderr unless
      * the syslog/stderr switch permits.  We don't do this until the
@@ -1230,6 +1237,11 @@
             StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
             PgStatPID = pgstat_start();

+        /* If we have lost the system logger, try to start a new one */
+        if (SysLoggerPID == 0 &&
+            StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+            SysLoggerPID = SysLogger_Start();
+
         /*
          * Touch the socket and lock file at least every ten minutes, to ensure
          * that they are not removed by overzealous /tmp-cleaning tasks.
@@ -1770,6 +1782,9 @@
             kill(BgWriterPID, SIGHUP);
         if (PgArchPID != 0)
             kill(PgArchPID, SIGHUP);
+        if (SysLoggerPID != 0)
+            kill(SysLoggerPID, SIGHUP);
+
         /* PgStatPID does not currently need SIGHUP */
         load_hba();
         load_ident();
@@ -1835,7 +1850,6 @@
             if (PgStatPID != 0)
                 kill(PgStatPID, SIGQUIT);
             break;
-
         case SIGINT:
             /*
              * Fast Shutdown:
@@ -1902,6 +1916,7 @@
                 kill(PgStatPID, SIGQUIT);
             if (DLGetHead(BackendList))
                 SignalChildren(SIGQUIT);
+
             ExitPostmaster(0);
             break;
     }
@@ -2059,6 +2074,15 @@
             continue;
         }

+        /* was it the system logger, try to start a new one */
+        if (SysLoggerPID != 0 && pid == SysLoggerPID)
+        {
+            if (exitstatus != 0)
+                LogChildExit(LOG, gettext("system logger process"),
+                             pid, exitstatus);
+            SysLoggerPID = SysLogger_Start();
+            continue;
+        }
         /*
          * Else do standard backend child cleanup.
          */
@@ -2956,6 +2980,16 @@
         PgstatCollectorMain(argc, argv);
         proc_exit(0);
     }
+    if (strcmp(argv[1], "-forklog") == 0)
+    {
+        /* Close the postmaster's sockets */
+        ClosePostmasterPorts();
+
+        /* Do not want to attach to shared memory */
+
+        SysLoggerMain(argc, argv);
+        proc_exit(0);
+    }

     return 1;                    /* shouldn't get here */
 }
@@ -3012,7 +3046,6 @@
         if (Shutdown <= SmartShutdown)
             SignalChildren(SIGUSR1);
     }
-
     if (PgArchPID != 0 && Shutdown == NoShutdown)
     {
         if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
@@ -3024,6 +3057,10 @@
             kill(PgArchPID, SIGUSR1);
         }
     }
+    if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+    {
+        kill(SysLoggerPID, SIGUSR1);
+    }

     PG_SETMASK(&UnBlockSig);

Index: src/backend/utils/adt/misc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/misc.c,v
retrieving revision 1.35
diff -u -r1.35 misc.c
--- src/backend/utils/adt/misc.c    2 Jul 2004 18:59:22 -0000    1.35
+++ src/backend/utils/adt/misc.c    21 Jul 2004 09:49:30 -0000
@@ -15,6 +15,7 @@
 #include "postgres.h"

 #include <sys/file.h>
+#include <unistd.h>
 #include <signal.h>
 #include <dirent.h>

@@ -26,6 +27,49 @@
 #include "funcapi.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_tablespace.h"
+#include "postmaster/syslogger.h"
+
+/*-----------------------
+ * some helper functions
+ */
+
+/*
+ * Return an absolute path. Argument may be absolute or
+ * relative to the DataDir.
+ */
+static char *absClusterPath(text *arg)
+{
+    char *filename;
+    int len=VARSIZE(arg) - VARHDRSZ;
+
+    filename = palloc(len+1);
+    memcpy(filename, VARDATA(arg), len);
+    filename[len] = 0;
+
+    if (is_absolute_path(filename))
+        return filename;
+    else
+    {
+        char *absname = palloc(strlen(DataDir)+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"))));
+}
+


 /*
@@ -109,18 +153,37 @@
     PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGINT));
 }

+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;
-} ts_db_fctx;
+} directory_fctx;

 Datum pg_tablespace_databases(PG_FUNCTION_ARGS)
 {
     FuncCallContext *funcctx;
     struct dirent *de;
-    ts_db_fctx *fctx;
+    directory_fctx *fctx;

     if (SRF_IS_FIRSTCALL())
     {
@@ -130,7 +193,7 @@
         funcctx=SRF_FIRSTCALL_INIT();
         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

-        fctx = palloc(sizeof(ts_db_fctx));
+        fctx = palloc(sizeof(directory_fctx));

         /*
          * size = path length + tablespace dirname length
@@ -164,7 +227,7 @@
     }

     funcctx=SRF_PERCALL_SETUP();
-    fctx = (ts_db_fctx*) funcctx->user_fctx;
+    fctx = (directory_fctx*) funcctx->user_fctx;

     if (!fctx->dirdesc)  /* not a tablespace */
         SRF_RETURN_DONE(funcctx);
@@ -202,3 +265,467 @@
     FreeDir(fctx->dirdesc);
     SRF_RETURN_DONE(funcctx);
 }
+
+
+
+/* ------------------------------------
+ * generic file handling functions
+ */
+
+
+Datum pg_file_stat(PG_FUNCTION_ARGS)
+{
+    AttInMetadata *attinmeta = NULL;
+    char *    filename = absClusterPath(PG_GETARG_TEXT_P(0));
+    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)));
+
+        PG_RETURN_NULL();
+    }
+
+    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));
+    pos = PG_GETARG_INT64(1);
+    size = PG_GETARG_INT64(2);
+
+    f = fopen(filename, "r");
+    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));
+    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, "w");
+    }
+    else
+        f = fopen(filename, "a");
+
+    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))
+            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();
+
+    fn1=absClusterPath(PG_GETARG_TEXT_P(0));
+    fn2=absClusterPath(PG_GETARG_TEXT_P(1));
+    if (PG_ARGISNULL(2))
+        fn3=0;
+    else
+        fn3=absClusterPath(PG_GETARG_TEXT_P(2));
+
+    struct stat fst;
+    if (stat(fn1, &fst) < 0)
+    {
+        ereport(WARNING,
+                (errcode_for_file_access(),
+                 errmsg("could not stat file %s: %m", fn1)));
+
+        PG_RETURN_BOOL(false);
+    }
+
+    if (fn3 && stat(fn2, &fst) < 0)
+    {
+        ereport(ERROR,
+                (errcode_for_file_access(),
+                 errmsg("could not stat file %s: %m", fn2)));
+
+        PG_RETURN_BOOL(false);
+    }
+
+
+    rc = stat(fn3 ? fn3 : fn2, &fst);
+    if (rc >= 0 || errno != ENOENT)
+    {
+        ereport(ERROR,
+                (ERRCODE_DUPLICATE_FILE,
+                 errmsg("cannot rename: target file %s exists", 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)));
+
+            }
+        }
+    }
+    if (rename(fn1, fn2) != 0)
+    {
+        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));
+
+    if (unlink(filename) < 0)
+    {
+        ereport(WARNING,
+                (errcode_for_file_access(),
+                 errmsg("could not unlink file %s", 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));
+
+        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);
+}
+
+
+/*
+ * logfile handling functions
+ */
+
+
+Datum pg_logfile_rotate(PG_FUNCTION_ARGS)
+{
+    requireSuperuser();
+
+    PG_RETURN_BOOL(LogFileRotate());
+}
+
+
+Datum pg_logdir_ls(PG_FUNCTION_ARGS)
+{
+    FuncCallContext *funcctx;
+    struct dirent *de;
+    directory_fctx *fctx;
+
+    requireSuperuser();
+
+    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(3, false);
+        TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
+                           TIMESTAMPOID, -1, 0);
+        TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
+                           INT4OID, -1, 0);
+        TupleDescInitEntry(tupdesc, (AttrNumber) 3, "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[3];
+        HeapTuple tuple;
+        int prefixLen=strlen(Log_filename_prefix);
+
+        char       *field[MAXDATEFIELDS];
+        char        lowstr[MAXDATELEN + 1];
+        int            dtype;
+        int            nf, ftype[MAXDATEFIELDS];
+        fsec_t        fsec;
+        int            tz = 0;
+        struct pg_tm date;
+        int         i;
+
+        /*
+         * format as created in logfile_getname():
+         *        prefix_YYYY-MM-DD_HHMMSS_PPPPP.log
+         *   prefixLen   ^
+         *                 prefixLen+17   ^
+         *                       prefixLen+23   ^
+         */
+
+        if (strlen(de->d_name) != prefixLen + 27
+            || memcmp(de->d_name, Log_filename_prefix, prefixLen)
+            || de->d_name[prefixLen + 17] != '_'
+            || strcmp(de->d_name + prefixLen + 23, ".log"))
+              continue;
+
+        values[2] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
+        sprintf(values[2], "%s/%s", fctx->location, de->d_name);
+
+        values[0] = de->d_name + prefixLen;       /* timestamp */
+        values[0][17] = 0;
+
+        values[1] = de->d_name + prefixLen + 18;  /* pid */
+        values[1][5] = 0;
+
+        /* check if pid is purely numeric as expected */
+        for (i = 0 ; i < 5 ; i++)
+            if (!isdigit(values[0][i]))
+                continue;
+
+        /* 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);
+}
Index: src/backend/utils/error/elog.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/error/elog.c,v
retrieving revision 1.142
diff -u -r1.142 elog.c
--- src/backend/utils/error/elog.c    24 Jun 2004 21:03:13 -0000    1.142
+++ src/backend/utils/error/elog.c    21 Jul 2004 09:49:33 -0000
@@ -84,6 +84,10 @@
 static void write_eventlog(int level, const char *line);
 #endif

+/* in syslogger.c */
+extern FILE *syslogFile;
+extern FILE *realStdErr;
+extern pid_t SysLoggerPID;
 /*
  * ErrorData holds the data accumulated during any one ereport() cycle.
  * Any non-NULL pointers must point to palloc'd data in ErrorContext.
@@ -1451,10 +1455,31 @@
         write_eventlog(eventlog_level, buf.data);
     }
 #endif   /* WIN32 */
-    /* Write to stderr, if enabled */
-    if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug)
+
+    /*
+     * Write to stderr. If Log_destination is file or stderr
+     * if file is target, the logger process will handle this
+     */
+    if ((Log_destination & (LOG_DESTINATION_STDERR | LOG_DESTINATION_FILE))
+        || whereToSendOutput == Debug)
     {
-        fprintf(stderr, "%s", buf.data);
+        if (SysLoggerPID == MyProcPid && realStdErr != 0)
+        {
+            /*
+             * If realStdErr is not null in the SysLogger process,
+             * there's something really wrong because stderr is probably
+             * redirected to the pipe. To avoid circular writes, we
+             * write to realStdErr which is hopefully the stderr the postmaster
+             * was started with.
+             */
+            fprintf(realStdErr, "%s", buf.data);
+        }
+        else
+            fprintf(stderr, "%s", buf.data) ;
+
+        /* syslogFile is open in SysLogger only */
+        if (syslogFile != 0)
+            fprintf(syslogFile, "%s", buf.data) ;
     }

     pfree(buf.data);
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.221
diff -u -r1.221 guc.c
--- src/backend/utils/misc/guc.c    19 Jul 2004 21:39:47 -0000    1.221
+++ src/backend/utils/misc/guc.c    21 Jul 2004 09:49:42 -0000
@@ -44,6 +44,7 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "postmaster/bgwriter.h"
+#include "postmaster/syslogger.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
@@ -1285,6 +1286,23 @@
         BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
     },

+    {
+      {"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur after n minutes"),
+       NULL
+      },
+      &Log_RotationAge,
+      24*60, 0, INT_MAX, NULL, NULL
+    },
+    {
+      {"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur if this size is reached (in kb)"),
+       NULL
+      },
+      &Log_RotationSize,
+      10*1024, 0, INT_MAX, NULL, NULL
+    },
+
     /* End-of-list marker */
     {
         {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
@@ -1627,13 +1645,32 @@
     {
         {"log_destination", PGC_POSTMASTER, LOGGING_WHERE,
          gettext_noop("Sets the target for log output."),
-         gettext_noop("Valid values are combinations of stderr, syslog "
+         gettext_noop("Valid values are combinations of stderr, file, syslog "
                       "and eventlog, depending on platform."),
          GUC_LIST_INPUT
         },
         &log_destination_string,
         "stderr", assign_log_destination, NULL
     },
+    {
+        {"log_directory", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("Sets the target directory for log output."),
+         gettext_noop("May be specified as relative to the cluster directory "
+                      "or as absolute path."),
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_directory,
+        "pg_log", NULL, NULL
+    },
+    {
+        {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("prefix for logfile names created in the log_directory."),
+         NULL,
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_filename_prefix,
+        "postgresql-", NULL, NULL
+    },

 #ifdef HAVE_SYSLOG
     {
@@ -5079,6 +5116,8 @@

         if (pg_strcasecmp(tok,"stderr") == 0)
             newlogdest |= LOG_DESTINATION_STDERR;
+        else if (pg_strcasecmp(tok,"file") == 0)
+            newlogdest |= LOG_DESTINATION_FILE;
 #ifdef HAVE_SYSLOG
         else if (pg_strcasecmp(tok,"syslog") == 0)
             newlogdest |= LOG_DESTINATION_SYSLOG;
Index: src/backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.116
diff -u -r1.116 postgresql.conf.sample
--- src/backend/utils/misc/postgresql.conf.sample    19 Jul 2004 02:47:10 -0000    1.116
+++ src/backend/utils/misc/postgresql.conf.sample    21 Jul 2004 09:49:43 -0000
@@ -167,9 +167,17 @@

 # - Where to Log -

-#log_destination = 'stderr'    # Valid values are combinations of stderr,
-                                # syslog and eventlog, depending on
-                                # platform.
+#log_destination = 'stderr' # Valid values are combinations of stderr, file,
+                            # syslog and eventlog, depending on platform.
+#log_directory = 'pg_log'   # subdirectory where logfiles are written
+                            # if 'file' log_destination is used.
+                            # May be specified absolute or relative to PGDATA
+#log_filename_prefix = 'postgresql_' # prefix for logfile names
+#log_rotation_age = 1440    # Automatic rotation of logfiles will happen if
+                            # specified age in minutes is reached. 0 to disable.
+#log_rotation_size = 10240  # Automatic rotation of logfiles will happen if
+                            # specified size in kb is reached. 0 to disable.
+
 #syslog_facility = 'LOCAL0'
 #syslog_ident = 'postgres'

Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.342
diff -u -r1.342 pg_proc.h
--- src/include/catalog/pg_proc.h    12 Jul 2004 20:23:53 -0000    1.342
+++ src/include/catalog/pg_proc.h    21 Jul 2004 09:49:57 -0000
@@ -2819,6 +2819,8 @@
 DESCR("Terminate a backend process");
 DATA(insert OID = 2172 ( pg_cancel_backend              PGNSP PGUID 12 f f t f s 1 23 "23" _null_ pg_cancel_backend -
_null_)); 
 DESCR("Cancel running query on a backend process");
+DATA(insert OID = 2173 ( pg_reload_conf                PGNSP PGUID 12 f f t f s 1 23 "" _null_ pg_reload_conf - _null_
));
+DESCR("Reload postgresql.conf");

 DATA(insert OID = 1946 (  encode                        PGNSP PGUID 12 f f t f i 2 25 "17 25" _null_  binary_encode -
_null_)); 
 DESCR("Convert bytea value into some ascii-only text string");
@@ -3607,6 +3609,26 @@
 DATA(insert OID = 2556 ( pg_tablespace_databases    PGNSP PGUID 12 f f t t s 1 26 "26" _null_ pg_tablespace_databases
-_null_)); 
 DESCR("returns database oids in a tablespace");

+DATA(insert OID = 2557( pg_file_stat                   PGNSP PGUID 12 f f t f v 1 2249 "25" _null_ pg_file_stat -
_null_)); 
+DESCR("stat properties of generic file");
+DATA(insert OID = 2558( pg_file_read                   PGNSP PGUID 12 f f t f v 3 25 "25 20 20" _null_ pg_file_read -
_null_)); 
+DESCR("read contents of generic file");
+DATA(insert OID = 2559( pg_file_write                   PGNSP PGUID 12 f f t f v 3 20 "25 25 16" _null_ pg_file_write
-_null_ )); 
+DESCR("write generic file");
+DATA(insert OID = 2560( pg_file_rename                PGNSP PGUID 12 f f t f v 2 16 "25 25" _null_ pg_file_rename -
_null_)); 
+DESCR("rename generic file");
+DATA(insert OID = 2561( pg_file_rename                PGNSP PGUID 12 f f t f v 33 16 "25 25 25" _null_ pg_file_rename
-_null_ )); 
+DESCR("rename generic file");
+DATA(insert OID = 2562( pg_file_unlink                   PGNSP PGUID 12 f f t f v 1 16 "25" _null_ pg_file_unlink -
_null_)); 
+DESCR("remove generic file");
+
+DATA(insert OID = 2563( pg_dir_ls                       PGNSP PGUID 12 f f t t v 2 25 "25 16" _null_ pg_dir_ls -
_null_)); 
+DESCR("list generic directory");
+
+DATA(insert OID = 2564( pg_logfile_rotate               PGNSP PGUID 12 f f t f v 0 16 "" _null_ pg_logfile_rotate -
_null_)); 
+DESCR("rotate log file");
+DATA(insert OID = 2565( pg_logdir_ls                   PGNSP PGUID 12 f f t t v 0 2249 "" _null_ pg_logdir_ls - _null_
));
+DESCR("list all available log files");

 /*
  * Symbolic values for provolatile column: these indicate whether the result
Index: src/include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v
retrieving revision 1.9
diff -u -r1.9 pmsignal.h
--- src/include/storage/pmsignal.h    19 Jul 2004 02:47:15 -0000    1.9
+++ src/include/storage/pmsignal.h    21 Jul 2004 09:49:58 -0000
@@ -25,7 +25,7 @@
     PMSIGNAL_PASSWORD_CHANGE,    /* pg_pwd file has changed */
     PMSIGNAL_WAKEN_CHILDREN,    /* send a SIGUSR1 signal to all backends */
     PMSIGNAL_WAKEN_ARCHIVER,    /* send a NOTIFY signal to xlog archiver */
-
+    PMSIGNAL_ROTATE_LOGFILE,    /* send SIGUSR1 to syslogger to rotate logfile */
     NUM_PMSIGNALS                /* Must be last value of enum! */
 } PMSignalReason;

Index: src/include/utils/builtins.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.246
diff -u -r1.246 builtins.h
--- src/include/utils/builtins.h    12 Jul 2004 20:23:59 -0000    1.246
+++ src/include/utils/builtins.h    21 Jul 2004 09:50:00 -0000
@@ -362,8 +362,20 @@
 extern Datum current_database(PG_FUNCTION_ARGS);
 extern Datum pg_terminate_backend(PG_FUNCTION_ARGS);
 extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
+extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
 extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);

+extern Datum pg_logfile_rotate(PG_FUNCTION_ARGS);
+extern Datum pg_logdir_ls(PG_FUNCTION_ARGS);
+
+extern Datum pg_file_stat(PG_FUNCTION_ARGS);
+extern Datum pg_file_read(PG_FUNCTION_ARGS);
+extern Datum pg_file_write(PG_FUNCTION_ARGS);
+extern Datum pg_file_rename(PG_FUNCTION_ARGS);
+extern Datum pg_file_unlink(PG_FUNCTION_ARGS);
+
+extern Datum pg_dir_ls(PG_FUNCTION_ARGS);
+
 /* not_in.c */
 extern Datum int4notin(PG_FUNCTION_ARGS);
 extern Datum oidnotin(PG_FUNCTION_ARGS);
Index: src/include/utils/elog.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/elog.h,v
retrieving revision 1.70
diff -u -r1.70 elog.h
--- src/include/utils/elog.h    6 Jul 2004 19:51:59 -0000    1.70
+++ src/include/utils/elog.h    21 Jul 2004 09:50:01 -0000
@@ -185,10 +185,10 @@
 #define LOG_DESTINATION_STDERR   1
 #define LOG_DESTINATION_SYSLOG   2
 #define LOG_DESTINATION_EVENTLOG 4
+#define LOG_DESTINATION_FILE     8

 /* Other exported functions */
 extern void DebugFileOpen(void);
-
 /*
  * Write errors to stderr (or by equal means when stderr is
  * not available). Used before ereport/elog can be used

pgsql-patches by date:

Previous
From: "Magnus Hagander"
Date:
Subject: Re: win32 readline
Next
From: "Mark Cave-Ayland"
Date:
Subject: Re: win32 readline