Re: [HACKERS] For review: Server instrumentation patch - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: [HACKERS] For review: Server instrumentation patch
Date
Msg-id 200508120327.j7C3RNX16963@candle.pha.pa.us
Whole thread Raw
In response to Re: [HACKERS] For review: Server instrumentation patch  (Bruce Momjian <pgman@candle.pha.pa.us>)
Responses Re: [HACKERS] For review: Server instrumentation patch  (Andreas Pflug <pgadmin@pse-consulting.de>)
Re: [HACKERS] For review: Server instrumentation patch  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-patches
Here is an updated patch I have just applied (catalog version updated).
I have added these functions:

        pg_stat_file()
        pg_read_file()
        pg_ls_dir()
        pg_reload_conf()
        pg_rotate_logfile()

I did not include pg_logdir_ls because that was basically pg_ls_dir with
logic to decode the log file name and convert it to a timestamp.  That
seemed best done in the client.

I also renamed a number of the functions to be use verb-noun, rather
than noun-verb.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/func.sgml,v
retrieving revision 1.276
diff -c -c -r1.276 func.sgml
*** doc/src/sgml/func.sgml    2 Aug 2005 16:11:56 -0000    1.276
--- doc/src/sgml/func.sgml    12 Aug 2005 03:18:29 -0000
***************
*** 9061,9066 ****
--- 9061,9069 ----
     <indexterm zone="functions-admin">
      <primary>pg_cancel_backend</primary>
     </indexterm>
+    <indexterm zone="functions-admin">
+     <primary>pg_reload_conf</primary>
+    </indexterm>

     <indexterm zone="functions-admin">
      <primary>signal</primary>
***************
*** 9068,9074 ****
     </indexterm>

     <para>
!     The function shown in <xref
      linkend="functions-admin-signal-table"> sends control signals to
      other server processes.  Use of this function is restricted
      to superusers.
--- 9071,9077 ----
     </indexterm>

     <para>
!     The functions shown in <xref
      linkend="functions-admin-signal-table"> sends control signals to
      other server processes.  Use of this function is restricted
      to superusers.
***************
*** 9090,9115 ****
         <entry><type>int</type></entry>
         <entry>Cancel a backend's current query</entry>
        </row>
       </tbody>
      </tgroup>
     </table>

     <para>
!     This function returns 1 if successful, 0 if not successful.
      The process ID (<literal>pid</literal>) of an active backend can be found
      from the <structfield>procpid</structfield> column in the
      <structname>pg_stat_activity</structname> view, or by listing the <command>postgres</command>
      processes on the server with <application>ps</>.
     </para>

     <indexterm zone="functions-admin">
      <primary>pg_start_backup</primary>
     </indexterm>
-
     <indexterm zone="functions-admin">
      <primary>pg_stop_backup</primary>
     </indexterm>
-
     <indexterm zone="functions-admin">
      <primary>backup</primary>
     </indexterm>
--- 9093,9128 ----
         <entry><type>int</type></entry>
         <entry>Cancel a backend's current query</entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_reload_conf</function>()</literal>
+         </entry>
+        <entry><type>int</type></entry>
+        <entry>Causes server processes to reload their configuration files</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>

     <para>
!     These functions return 1 if successful, 0 if not successful.
      The process ID (<literal>pid</literal>) of an active backend can be found
      from the <structfield>procpid</structfield> column in the
      <structname>pg_stat_activity</structname> view, or by listing the <command>postgres</command>
      processes on the server with <application>ps</>.
     </para>
+    <para>
+     <function>pg_reload_conf</> sends a SIGHUP signal to the
+     postmaster, causing the reload of the configuration files
+     in all backend processes.
+    </para>

     <indexterm zone="functions-admin">
      <primary>pg_start_backup</primary>
     </indexterm>
     <indexterm zone="functions-admin">
      <primary>pg_stop_backup</primary>
     </indexterm>
     <indexterm zone="functions-admin">
      <primary>backup</primary>
     </indexterm>
***************
*** 9309,9314 ****
--- 9322,9434 ----
      appropriate.
     </para>

+    <para>
+     The functions shown in <xref
+     linkend="functions-admin-genfile"> provide native file access to
+     files on the machine hosting the server. Only files relative to
+     the cluster directory are allowed, and the logfile directory,
+     because the logfile directory might be stored outside the
+     cluster directory.  Use of these functions is restricted to
+     superusers.
+    </para>
+
+    <table id="functions-admin-genfile">
+     <title>Generic File Access Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry>
+       </row>
+      </thead>
+
+      <tbody>
+       <row>
+        <entry>
+         <literal><function>pg_file_length</function>(<parameter>filename_text</parameter>)</literal>
+          <indexterm zone="functions-admin">
+           <primary>pg_file_length</primary>
+         </indexterm>
+        </entry>
+        <entry><type>int8</type></entry>
+        <entry>Returns the file length</entry>
+       </row>
+       <row>
+        <entry>
+
<literal><function>pg_ls_dir</function>(<parameter>dirname_text</parameter>,<parameter>fullpath_bool</parameter>)</literal>
+         <indexterm zone="functions-admin">
+          <primary>pg_ls_dir</primary>
+         </indexterm>
+        </entry>
+        <entry><type>setof text</type></entry>
+        <entry>List the contents of a directory</entry>
+       </row>
+       <row>
+        <entry>
+         <literal><function>pg_read_file</function>(<parameter>filename_text</parameter>,
+         <parameter>offset_int8</parameter>,<parameter>length_int8</parameter>)</literal>
+        </entry>
+        <entry><type>text</type></entry>
+        <entry>Returns the contents of a text file</entry>
+       </row>
+       <row>
+        <entry>
+         <literal><function>pg_stat_file</function>(<parameter>filename_text</parameter>)</literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns information about the file</entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+
+    <indexterm zone="functions-admin">
+     <primary>pg_read_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_read_file()</> returns part of a textfile, starting
+     at the given offset, returning length bytes.  If offset is negative,
+     it is treated relative to the end of the file.
+    </para>
+
+    <indexterm zone="functions-admin">
+     <primary>pg_stat_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_stat_file()</> returns a record containing the
+     length, last accessed timestamp, last modified timestamp,
+     creation timestamp, and a flag indicating if it is a directory.
+    </para>
+
+    <para>
+     The function shown in <xref
+     linkend="functions-admin-logfile"> forces the server
+     logfile to be rotated if <varname>redirect_stderr</>
+     is used for logging.   Use of this functions is restricted
+     to superusers.
+    </para>
+
+    <table id="functions-admin-logfile">
+     <title>Backend Logfile Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry>
+       </row>
+      </thead>
+
+      <tbody>
+       <row>
+        <entry>
+         <literal><function>pg_rotate_logfile</function>()</literal>
+         <indexterm zone="functions-admin">
+          <primary>pg_rotate_logfile</primary>
+         </indexterm>
+         </entry>
+        <entry><type>int</type></entry>
+        <entry>Rotate logfile</entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+
    </sect1>
  </chapter>

Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.462
diff -c -c -r1.462 postmaster.c
*** src/backend/postmaster/postmaster.c    11 Aug 2005 21:11:44 -0000    1.462
--- src/backend/postmaster/postmaster.c    12 Aug 2005 03:18:32 -0000
***************
*** 3394,3399 ****
--- 3394,3402 ----
          }
      }

+     if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+         kill(SysLoggerPID, SIGUSR1);
+
      PG_SETMASK(&UnBlockSig);

      errno = save_errno;
Index: src/backend/postmaster/syslogger.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/postmaster/syslogger.c,v
retrieving revision 1.18
diff -c -c -r1.18 syslogger.c
*** src/backend/postmaster/syslogger.c    21 Jul 2005 18:06:12 -0000    1.18
--- src/backend/postmaster/syslogger.c    12 Aug 2005 03:18:33 -0000
***************
*** 101,106 ****
--- 101,107 ----
   * Flags set by interrupt handlers for later service in the main loop.
   */
  static volatile sig_atomic_t got_SIGHUP = false;
+ static volatile sig_atomic_t rotation_requested = false;


  /* Local subroutines */
***************
*** 117,122 ****
--- 118,124 ----
  static char *logfile_getname(pg_time_t timestamp);
  static void set_next_rotation_time(void);
  static void sigHupHandler(SIGNAL_ARGS);
+ static void sigUsr1Handler(SIGNAL_ARGS);


  /*
***************
*** 200,206 ****
      pqsignal(SIGQUIT, SIG_IGN);
      pqsignal(SIGALRM, SIG_IGN);
      pqsignal(SIGPIPE, SIG_IGN);
!     pqsignal(SIGUSR1, SIG_IGN);
      pqsignal(SIGUSR2, SIG_IGN);

      /*
--- 202,208 ----
      pqsignal(SIGQUIT, SIG_IGN);
      pqsignal(SIGALRM, SIG_IGN);
      pqsignal(SIGPIPE, SIG_IGN);
!     pqsignal(SIGUSR1, sigUsr1Handler);  /* request log rotation */
      pqsignal(SIGUSR2, SIG_IGN);

      /*
***************
*** 235,241 ****
      /* main worker loop */
      for (;;)
      {
-         bool        rotation_requested = false;
          bool        time_based_rotation = false;

  #ifndef WIN32
--- 237,242 ----
***************
*** 726,731 ****
--- 727,734 ----
      char       *filename;
      FILE       *fh;

+     rotation_requested = false;
+
      /*
       * When doing a time-based rotation, invent the new logfile name based
       * on the planned rotation time, not current time, to avoid "slippage"
***************
*** 876,878 ****
--- 879,888 ----
  {
      got_SIGHUP = true;
  }
+
+ /* SIGUSR1: set flag to rotate logfile */
+ static void
+ sigUsr1Handler(SIGNAL_ARGS)
+ {
+     rotation_requested = true;
+ }
Index: src/backend/utils/adt/Makefile
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v
retrieving revision 1.58
diff -c -c -r1.58 Makefile
*** src/backend/utils/adt/Makefile    29 Jul 2005 14:46:57 -0000    1.58
--- src/backend/utils/adt/Makefile    12 Aug 2005 03:18:33 -0000
***************
*** 24,30 ****
      tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
      network.o mac.o inet_net_ntop.o inet_net_pton.o \
      ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
!     ascii.o quote.o pgstatfuncs.o encode.o dbsize.o

  like.o: like.c like_match.c

--- 24,30 ----
      tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
      network.o mac.o inet_net_ntop.o inet_net_pton.o \
      ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
!     ascii.o quote.o pgstatfuncs.o encode.o dbsize.o genfile.o

  like.o: like.c like_match.c

Index: src/backend/utils/adt/misc.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/misc.c,v
retrieving revision 1.45
diff -c -c -r1.45 misc.c
*** src/backend/utils/adt/misc.c    4 Jul 2005 04:51:50 -0000    1.45
--- src/backend/utils/adt/misc.c    12 Aug 2005 03:18:33 -0000
***************
*** 21,34 ****
--- 21,43 ----
  #include "commands/dbcommands.h"
  #include "miscadmin.h"
  #include "storage/procarray.h"
+ #include "storage/pmsignal.h"
  #include "storage/fd.h"
  #include "utils/builtins.h"
+ #include "utils/elog.h"
  #include "funcapi.h"
  #include "catalog/pg_type.h"
  #include "catalog/pg_tablespace.h"
+ #include "postmaster/syslogger.h"

  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))

+ typedef struct
+ {
+     char    *location;
+     DIR        *dirdesc;
+ } directory_fctx;
+

  /*
   * Check if data is Null
***************
*** 107,112 ****
--- 116,166 ----
      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("must be superuser to 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);
+ }
+
+
+ /*
+  * Rotate log file
+  */
+ Datum
+ pg_rotate_logfile(PG_FUNCTION_ARGS)
+ {
+     if (!superuser())
+         ereport(ERROR,
+                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                  (errmsg("must be superuser to rotate log files"))));
+
+     if (!Redirect_stderr)
+     {
+         ereport(NOTICE,
+                 (errcode(ERRCODE_WARNING),
+                  errmsg("no logfile configured; rotation not supported")));
+         PG_RETURN_INT32(0);
+     }
+
+     SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
+
+     PG_RETURN_INT32(0);
+ }
+
  #ifdef NOT_USED

  /* Disabled in 8.0 due to reliability concerns; FIXME someday */
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.380
diff -c -c -r1.380 pg_proc.h
*** src/include/catalog/pg_proc.h    2 Aug 2005 16:11:57 -0000    1.380
--- src/include/catalog/pg_proc.h    12 Aug 2005 03:18:41 -0000
***************
*** 3049,3054 ****
--- 3049,3071 ----
  DATA(insert OID = 2173 ( pg_stop_backup            PGNSP PGUID 12 f f t f v 0 25 "" _null_ _null_ _null_
pg_stop_backup- _null_ )); 
  DESCR("Finish taking an online backup");

+ DATA(insert OID = 2621 ( pg_reload_conf         PGNSP PGUID 12 f f t f v 0 23 "" _null_ _null_ _null_ pg_reload_conf
-_null_ )); 
+ DESCR("Reload configuration files");
+
+ DATA(insert OID = 2622 ( pg_rotate_logfile        PGNSP PGUID 12 f f t f v 0 23 "" _null_ _null_ _null_
pg_rotate_logfile- _null_ )); 
+ DESCR("Rotate log file");
+
+
+ DATA(insert OID = 2623 ( pg_stat_file        PGNSP PGUID 12 f f t f v 1 2249 "25" _null_ _null_ _null_ pg_stat_file -
_null_)); 
+ DESCR("Return file information");
+ DATA(insert OID = 2624 ( pg_file_length        PGNSP PGUID 14 f f t f v 1 20 "25" _null_ _null_ _null_ "SELECT len
FROMpg_stat_file($1) AS s(len int8, c timestamp, a timestamp, m timestamp, i bool)" - _null_ )); 
+ DESCR("Return file length");
+ DATA(insert OID = 2625 ( pg_read_file        PGNSP PGUID 12 f f t f v 3 25 "25 20 20" _null_ _null_ _null_
pg_read_file- _null_ )); 
+ DESCR("Read text from a file");
+ DATA(insert OID = 2626 ( pg_ls_dir            PGNSP PGUID 12 f f t t v 1 25 "25" _null_ _null_ _null_ pg_ls_dir -
_null_)); 
+ DESCR("List all file in a directory");
+
+
  /* Aggregates (moved here from pg_aggregate for 7.3) */

  DATA(insert OID = 2100 (  avg                PGNSP PGUID 12 t f f f i 1 1700 "20" _null_ _null_ _null_
aggregate_dummy- _null_ )); 
Index: src/include/storage/pmsignal.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/storage/pmsignal.h,v
retrieving revision 1.12
diff -c -c -r1.12 pmsignal.h
*** src/include/storage/pmsignal.h    28 Jun 2005 19:51:25 -0000    1.12
--- src/include/storage/pmsignal.h    12 Aug 2005 03:18:42 -0000
***************
*** 25,30 ****
--- 25,31 ----
      PMSIGNAL_PASSWORD_CHANGE,    /* pg_auth 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: /cvsroot/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.262
diff -c -c -r1.262 builtins.h
*** src/include/utils/builtins.h    29 Jul 2005 14:47:04 -0000    1.262
--- src/include/utils/builtins.h    12 Aug 2005 03:18:42 -0000
***************
*** 374,385 ****
--- 374,392 ----
  extern Datum pg_complete_relation_size_name(PG_FUNCTION_ARGS);
  extern Datum pg_size_pretty(PG_FUNCTION_ARGS);

+ /* genfile.c */
+ extern Datum pg_stat_file(PG_FUNCTION_ARGS);
+ extern Datum pg_read_file(PG_FUNCTION_ARGS);
+ extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+
  /* misc.c */
  extern Datum nullvalue(PG_FUNCTION_ARGS);
  extern Datum nonnullvalue(PG_FUNCTION_ARGS);
  extern Datum current_database(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_rotate_logfile(PG_FUNCTION_ARGS);

  /* not_in.c */
  extern Datum int4notin(PG_FUNCTION_ARGS);
/*-------------------------------------------------------------------------
 *
 * genfile.c
 *
 *
 * Copyright (c) 2004, PostgreSQL Global Development Group
 *
 * Author: Andreas Pflug <pgadmin@pse-consulting.de>
 *
 * IDENTIFICATION
 *      $PostgreSQL: $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>

#include "utils/builtins.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "catalog/pg_type.h"
#include "funcapi.h"

extern  char *Log_directory;

typedef struct
{
    char    *location;
    DIR        *dirdesc;
} directory_fctx;

/*
 * Return an absolute path. Argument may be absolute or
 * relative to the DataDir.
 */
static char *check_and_make_absolute(text *arg)
{
    char *filename;
    int filename_len = VARSIZE(arg) - VARHDRSZ;
    int datadir_len = strlen(DataDir);

    filename = palloc(filename_len + 1);
    memcpy(filename, VARDATA(arg), filename_len);
    filename[filename_len] = '\0';

    canonicalize_path(filename);
    filename_len = strlen(filename);    /* recompute */

    /*
     *    Prevent reference to the parent directory.
     *    "..a.." is a valid file name though.
     */
    if (strcmp(filename, "..") == 0 ||                            /* beginning */
        strncmp(filename, "../", 3) == 0 ||                        /* beginning */
        strcmp(filename, "/..") == 0 ||                            /* beginning */
        strncmp(filename, "../", 3) == 0 ||                        /* beginning */
        strstr(filename, "/../") != NULL ||                        /* middle */
        strncmp(filename + filename_len - 3, "/..", 3) == 0)    /* end */
            ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   (errmsg("Reference to a parent directory (\"..\") not allowed"))));

    if (is_absolute_path(filename))
    {
        /* The log directory might be outside our datadir, but allow it */
        if ((strncmp(filename, Log_directory, strlen(Log_directory)) == 0 &&
             (filename[strlen(Log_directory)] == '/' ||
              filename[strlen(Log_directory)] == '\0')) ||
            (strncmp(filename, DataDir, datadir_len) == 0 &&
             (filename[datadir_len] == '/' ||
              filename[datadir_len] == '\0')))
            return filename;

        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 (errmsg("Absolute path not allowed"))));
        return NULL;
    }
    else
    {
        char *absname = palloc(datadir_len + filename_len + 2);
        sprintf(absname, "%s/%s", DataDir, filename);
        pfree(filename);
        return absname;
    }
}


Datum pg_file_read(PG_FUNCTION_ARGS)
{
    size_t        bytes_to_read = PG_GETARG_INT64(2);
    int64        seek_offset = PG_GETARG_INT64(1);
    char         *buf = 0;
    size_t        nbytes;
    FILE        *file;
    char        *filename;

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 (errmsg("only superuser may access generic file functions"))));

    filename = check_and_make_absolute(PG_GETARG_TEXT_P(0));

    if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
    {
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not open file %s for reading: %m", filename)));
        PG_RETURN_NULL();
    }

    if (fseeko(file, (off_t)seek_offset,
        (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
    {
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not seek in file %s: %m", filename)));
        PG_RETURN_NULL();
    }

    buf = palloc(bytes_to_read + VARHDRSZ);

    nbytes = fread(VARDATA(buf), bytes_to_read, 1, file);
    FreeFile(file);

    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;

    pfree(filename);
    PG_RETURN_TEXT_P(buf);
}


Datum pg_file_stat(PG_FUNCTION_ARGS)
{
    AttInMetadata *attinmeta;
    char        *filename = check_and_make_absolute(PG_GETARG_TEXT_P(0));
    struct stat fst;
    char        lenbuf[30], cbuf[30], abuf[30], mbuf[30], dirbuf[2];
    char        *values[5] = {lenbuf, cbuf, abuf, mbuf, dirbuf};
    pg_time_t    timestamp;
    HeapTuple    tuple;
    TupleDesc    tupdesc = CreateTemplateTupleDesc(5, false);

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 (errmsg("only superuser may access generic file functions"))));

    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)));
        MemSet(values, 0, sizeof(char *) * 5);
        tuple = BuildTupleFromCStrings(attinmeta, values);
    }
    else
    {
        snprintf(lenbuf, 30, INT64_FORMAT, (int64)fst.st_size);

        timestamp = fst.st_ctime;
        pg_strftime(cbuf, 30, "%F %T", pg_localtime(×tamp, global_timezone));

        timestamp = fst.st_atime;
        pg_strftime(abuf, 30, "%F %T", pg_localtime(×tamp, global_timezone));

        timestamp = fst.st_mtime;
        pg_strftime(mbuf, 30, "%F %T", pg_localtime(×tamp, global_timezone));

        if (fst.st_mode & S_IFDIR)
            strcpy(dirbuf, "t");
        else
            strcpy(dirbuf, "f");

        tuple = BuildTupleFromCStrings(attinmeta, values);
    }

    pfree(filename);
    PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
}


Datum pg_dir_ls(PG_FUNCTION_ARGS)
{
    FuncCallContext    *funcctx;
    struct dirent    *de;
    directory_fctx    *fctx;

    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 (errmsg("only superuser may access generic file functions"))));

    if (SRF_IS_FIRSTCALL())
    {
        MemoryContext oldcontext;

        funcctx = SRF_FIRSTCALL_INIT();
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

        fctx = palloc(sizeof(directory_fctx));
        fctx->location = check_and_make_absolute(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 = NULL;
        }
        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, fctx->location)) != 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);
}

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Bug in canonicalize_path()
Next
From: "William ZHANG"
Date:
Subject: Re: Bug in canonicalize_path()