- Updated to current cvs.
- pg_terminate_backend omitted
- contrib/dbsize cleaned to reflect move of functions to core
Index: contrib/dbsize/README.dbsize
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/dbsize/README.dbsize,v
retrieving revision 1.5
diff -u -r1.5 README.dbsize
--- contrib/dbsize/README.dbsize 26 Feb 2005 23:31:15 -0000 1.5
+++ contrib/dbsize/README.dbsize 21 Jun 2005 09:24:09 -0000
@@ -1,21 +1,15 @@
This module contains several functions that report the on-disk size of a
-given database object in bytes:
+given database object in bytes. They extend the builtin object size functions.
+
int8 database_size(name)
int8 relation_size(text)
int8 indexes_size(text)
int8 total_relation_size(text)
-
- int8 pg_database_size(oid)
- int8 pg_relation_size(oid)
- int8 pg_tablespace_size(oid)
-
- text pg_size_pretty(int8)
-
setof record relation_size_components(text)
-The first four functions take the name of the object (possibly
-schema-qualified for the latter three) and returns the size of the
+These functions take the name of the object (possibly
+schema-qualified for the second) and returns the size of the
on-disk files in bytes.
SELECT database_size('template1');
@@ -23,11 +17,6 @@
SELECT indexes_size('pg_class');
SELECT total_relation_size('pg_class');
-These functions take object OIDs:
-
- SELECT pg_database_size(1); -- template1 database
- SELECT pg_relation_size(1259); -- pg_class table size
- SELECT pg_tablespace_size(1663); -- pg_default tablespace
The indexes_size() function returns the total size of the indices for a
relation, including any toasted indices.
@@ -35,7 +24,7 @@
The total_relation_size() function returns the total size of the relation,
all its indices, and any toasted data.
-Please note that relation_size and pg_relation_size report only the size of
+Please note that relation_size reports only the size of
the selected relation itself; any related indexes or toast tables are not
counted. To obtain the total size of a table including all indices and
toasted data, use total_relation_size().
Index: contrib/dbsize/dbsize.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/dbsize/dbsize.c,v
retrieving revision 1.18
diff -u -r1.18 dbsize.c
--- contrib/dbsize/dbsize.c 19 Jun 2005 21:34:00 -0000 1.18
+++ contrib/dbsize/dbsize.c 21 Jun 2005 09:24:09 -0000
@@ -27,19 +27,9 @@
/* hack to make it compile under Win32 */
extern DLLIMPORT char *DataDir;
-Datum pg_tablespace_size(PG_FUNCTION_ARGS);
-Datum pg_database_size(PG_FUNCTION_ARGS);
-Datum pg_relation_size(PG_FUNCTION_ARGS);
-Datum pg_size_pretty(PG_FUNCTION_ARGS);
-
Datum database_size(PG_FUNCTION_ARGS);
Datum relation_size(PG_FUNCTION_ARGS);
-PG_FUNCTION_INFO_V1(pg_tablespace_size);
-PG_FUNCTION_INFO_V1(pg_database_size);
-PG_FUNCTION_INFO_V1(pg_relation_size);
-PG_FUNCTION_INFO_V1(pg_size_pretty);
-
PG_FUNCTION_INFO_V1(database_size);
PG_FUNCTION_INFO_V1(relation_size);
@@ -120,64 +110,6 @@
return totalsize;
}
-/*
- * calculate total size of tablespace
- */
-Datum
-pg_tablespace_size(PG_FUNCTION_ARGS)
-{
- Oid tblspcOid = PG_GETARG_OID(0);
- char tblspcPath[MAXPGPATH];
- char pathname[MAXPGPATH];
- int64 totalsize=0;
- DIR *dirdesc;
- struct dirent *direntry;
-
- if (tblspcOid == DEFAULTTABLESPACE_OID)
- snprintf(tblspcPath, MAXPGPATH, "%s/base", DataDir);
- else if (tblspcOid == GLOBALTABLESPACE_OID)
- snprintf(tblspcPath, MAXPGPATH, "%s/global", DataDir);
- else
- snprintf(tblspcPath, MAXPGPATH, "%s/pg_tblspc/%u", DataDir, tblspcOid);
-
- dirdesc = AllocateDir(tblspcPath);
-
- while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
- {
- struct stat fst;
-
- if (strcmp(direntry->d_name, ".") == 0 ||
- strcmp(direntry->d_name, "..") == 0)
- continue;
-
- snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
-
- if (stat(pathname, &fst) < 0)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not stat \"%s\": %m", pathname)));
- totalsize += fst.st_size;
-
- if (fst.st_mode & S_IFDIR)
- totalsize += db_dir_size(pathname);
- }
-
- FreeDir(dirdesc);
-
- PG_RETURN_INT64(totalsize);
-}
-
-
-/*
- * calculate size of database in all tablespaces
- */
-Datum
-pg_database_size(PG_FUNCTION_ARGS)
-{
- Oid dbOid = PG_GETARG_OID(0);
-
- PG_RETURN_INT64(calculate_database_size(dbOid));
-}
Datum
database_size(PG_FUNCTION_ARGS)
@@ -241,34 +173,6 @@
return totalsize;
}
-/*
- * calculate size of relation
- */
-Datum
-pg_relation_size(PG_FUNCTION_ARGS)
-{
- Oid relOid=PG_GETARG_OID(0);
- HeapTuple tuple;
- Form_pg_class pg_class;
- Oid relnodeOid;
- Oid tblspcOid;
-
- tuple = SearchSysCache(RELOID,
- ObjectIdGetDatum(relOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- ereport(ERROR,
- (ERRCODE_UNDEFINED_TABLE,
- errmsg("relation with OID %u does not exist", relOid)));
-
- pg_class = (Form_pg_class) GETSTRUCT(tuple);
- relnodeOid = pg_class->relfilenode;
- tblspcOid = pg_class->reltablespace;
-
- ReleaseSysCache(tuple);
-
- PG_RETURN_INT64(calculate_relation_size(tblspcOid, relnodeOid));
-}
Datum
relation_size(PG_FUNCTION_ARGS)
@@ -289,50 +193,3 @@
PG_RETURN_INT64(calculate_relation_size(tblspcOid, relnodeOid));
}
-
-/*
- * formatting with size units
- */
-Datum
-pg_size_pretty(PG_FUNCTION_ARGS)
-{
- int64 size=PG_GETARG_INT64(0);
- char *result=palloc(50+VARHDRSZ);
- int64 limit = 10*1024;
- int64 mult=1;
-
- if (size < limit*mult)
- snprintf(VARDATA(result), 50, INT64_FORMAT" bytes",
- size);
- else
- {
- mult *= 1024;
- if (size < limit*mult)
- snprintf(VARDATA(result), 50, INT64_FORMAT " kB",
- (size+mult/2) / mult);
- else
- {
- mult *= 1024;
- if (size < limit*mult)
- snprintf(VARDATA(result), 50, INT64_FORMAT " MB",
- (size+mult/2) / mult);
- else
- {
- mult *= 1024;
- if (size < limit*mult)
- snprintf(VARDATA(result), 50, INT64_FORMAT " GB",
- (size+mult/2) / mult);
- else
- {
- mult *= 1024;
- snprintf(VARDATA(result), 50, INT64_FORMAT " TB",
- (size+mult/2) / mult);
- }
- }
- }
- }
-
- VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ;
-
- PG_RETURN_TEXT_P(result);
-}
Index: contrib/dbsize/dbsize.sql.in
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/dbsize/dbsize.sql.in,v
retrieving revision 1.5
diff -u -r1.5 dbsize.sql.in
--- contrib/dbsize/dbsize.sql.in 26 Feb 2005 23:31:15 -0000 1.5
+++ contrib/dbsize/dbsize.sql.in 21 Jun 2005 09:24:10 -0000
@@ -6,22 +6,6 @@
AS 'MODULE_PATHNAME', 'relation_size'
LANGUAGE C STRICT;
-CREATE FUNCTION pg_tablespace_size(oid) RETURNS bigint
- AS 'MODULE_PATHNAME', 'pg_tablespace_size'
- LANGUAGE C STRICT;
-
-CREATE FUNCTION pg_database_size(oid) RETURNS bigint
- AS 'MODULE_PATHNAME', 'pg_database_size'
- LANGUAGE C STRICT;
-
-CREATE FUNCTION pg_relation_size(oid) RETURNS bigint
- AS 'MODULE_PATHNAME', 'pg_relation_size'
- LANGUAGE C STRICT;
-
-CREATE FUNCTION pg_size_pretty(bigint) RETURNS text
- AS 'MODULE_PATHNAME', 'pg_size_pretty'
- LANGUAGE C STRICT;
-
CREATE FUNCTION total_relation_size (text) RETURNS bigint AS '
SELECT pg_relation_size(r.oid)
+ COALESCE(pg_relation_size(t.oid), 0)::bigint
@@ -129,4 +113,3 @@
AND idxr.reltoastrelid = idxtoast.oid
AND idxtoast.reltoastrelid = idxtoastidxr.oid
' LANGUAGE SQL;
-
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/func.sgml,v
retrieving revision 1.258
diff -u -r1.258 func.sgml
--- doc/src/sgml/func.sgml 15 Jun 2005 06:29:25 -0000 1.258
+++ doc/src/sgml/func.sgml 21 Jun 2005 09:24:32 -0000
@@ -8916,6 +8916,9 @@
<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>
@@ -8923,7 +8926,7 @@
</indexterm>
<para>
- The function shown in <xref
+ 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.
@@ -8945,21 +8948,29 @@
<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>Triggers the server processes to reload configuration files</entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
- This function returns 1 if successful, 0 if not successful.
+ 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>
-
- <indexterm zone="functions-admin">
- <primary>pg_start_backup</primary>
- </indexterm>
+ <para>
+ <function>pg_reload_conf</> sends a SIGHUP event to the
+ postmaster, and thus triggers a reload of the configuration files
+ in all backend processes.
+ </para>
<indexterm zone="functions-admin">
<primary>pg_stop_backup</primary>
@@ -9026,6 +9037,272 @@
For details about proper usage of these functions, see
<xref linkend="backup-online">.
</para>
+
+ <para>
+ The functions shown in <xref
+ linkend="functions-admin-dbsize"> calculate the actual disk space
+ usage of database objects.
+ </para>
+
+ <table id="functions-admin-dbsize">
+ <title>Database Object Size 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_tablespace_size</function>(<parameter>tablespace_oid</parameter>)</literal>
+ </entry>
+ <entry><type>int8</type></entry>
+ <entry>Calculates total disk space of a tablespace</entry>
+ </row>
+ <row>
+ <entry>
+ <literal><function>pg_database_size</function>(<parameter>database_oid</parameter>)</literal>
+ </entry>
+ <entry><type>int8</type></entry>
+ <entry>Calculates total disk space of a database</entry>
+ </row>
+ <row>
+ <entry>
+ <literal><function>pg_relation_size</function>(<parameter>relation_oid</parameter>)</literal>
+ </entry>
+ <entry><type>int8</type></entry>
+ <entry>Calculates total disk space of a relation</entry>
+ </row>
+ <row>
+ <entry>
+ <literal><function>pg_size_pretty</function>(<parameter>size_int8</parameter>)</literal>
+ </entry>
+ <entry><type>text</type></entry>
+ <entry>Formats the size in a human readable format with size units </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ <function>pg_tablespace_size</>, <function>pg_database_size</>
+ and <function>pg_relation_size</> accept an oid, and calculate
+ the disk space usage of the object.
+ </para>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_relation_size</primary>
+ </indexterm>
+ <para>
+ <function>pg_relation_size</> accepts oids of standard tables, indexes and
+ toast tables. To calculate the disk space usage of a table
+ including all its indexes and toast tables, these components need
+ to be summed up.
+ </para>
+ <para>
+ <function>pg_size_pretty</> can be used to format the size of the
+ database objects in a human readable way, using kB, MB, GB or TB as appropriate.
+ </para>
+
+ <para>
+ The functions shown in <xref
+ linkend="functions-admin-genfile"> provide native file access to
+ files on the machine hosting the server. They are restricted to
+ the cluster directory or the logfile 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_stat</function>(<parameter>filename_text</parameter>)</literal>
+ </entry>
+ <entry><type>record</type></entry>
+ <entry>Retrieves file stats</entry>
+ </row>
+ <row>
+ <entry>
+ <literal><function>pg_file_length</function>(<parameter>filename_text</parameter>)</literal>
+ </entry>
+ <entry><type>int8</type></entry>
+ <entry>Returns the file length</entry>
+ </row>
+ <row>
+ <entry>
+ <literal><function>pg_file_read</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_file_write</function>(<parameter>filename_text</parameter>,<parameter>offset_int8></parameter>,<parameter>append_bool</parameter>)</literal>
+ </entry>
+ <entry><type>int8</type></entry>
+ <entry>Writes text to a file</entry>
+ </row>
+ <row>
+ <entry>
+
<literal><function>pg_file_rename</function>(<parameter>oldname_text</parameter>,<parameter>newname_text</parameter>)</literal>
+ </entry>
+ <entry><type>bool</type></entry>
+ <entry>Renames a file</entry>
+ </row>
+ <row>
+ <entry>
+
<literal><function>pg_file_rename</function>(<parameter>oldname_text</parameter>,<parameter>newname_text</parameter>,<parameter>archivname_text</parameter>)</literal>
+ </entry>
+ <entry><type>bool</type></entry>
+ <entry>Renames a file and its archive file</entry>
+ </row>
+ <row>
+ <entry>
+ <literal><function>pg_file_unlink</function>(<parameter>filename_text</parameter>)</literal>
+ </entry>
+ <entry><type>bool</type></entry>
+ <entry>Returns the file length</entry>
+ </row>
+ <row>
+ <entry>
+
<literal><function>pg_dir_ls</function>(<parameter>dirname_text</parameter>,<parameter>fullpath_bool</parameter>)</literal>
+ </entry>
+ <entry><type>setof text</type></entry>
+ <entry>Returns the file length</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_file_stat</primary>
+ </indexterm>
+
+ <para>
+ <function>pg_file_stat()</> returns a record that contains the
+ length, creation timestamp, last accessed timestamp, last modified
+ timestamp and and a flag indicating a directory.
+ </para>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_file_length</primary>
+ </indexterm>
+ <para>
+ <function>pg_file_length()</> returns the length of the given file.
+ </para>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_file_read</primary>
+ </indexterm>
+ <para>
+ <function>pg_file_read()</> returns a part of a textfile, starting
+ at the offset giving length bytes.
+
+ <indexterm zone="functions-admin">
+ <primary>pg_file_write</primary>
+ </indexterm>
+ <para>
+ <function>pg_file_write()</> writes text to a file at the given
+ offset. If the append flag is false, the file may not exist. If
+ the append file is set, the file must exist and be accessible.
+ </para>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_file_rename</primary>
+ </indexterm>
+ <para>
+ <function>pg_file_rename(oldname_text,newname_text)</> is used to
+ rename a file to a new name.
+ </para>
+ <para>
+ <function>pg_file_rename(oldname_text,newname_text,archivname_text)</> is used to
+ rename a file and its backup file to new names. The
+ oldname and newname files must exist, while the archivname file
+ may not exist when this command is issued.This can be used
+ to replace important files in a relatively safe way, because the
+ function will try to do the whole renaming or nothing at all.
+ Example:
+ <programlisting>
+ select pg_file_rename('postgresql.conf.tmp', 'postgresql.conf', 'postgresql.conf.bak');
+ </programlisting>
+ will replace postgresql.conf with a new version which was
+ previously written to the temporary file postgresql.conf.tmp,
+ saving the former version as postgresql.conf.bak.
+ </para>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_file_unlink</primary>
+ </indexterm>
+ <para>
+ <function>pg_file_unlink()</> unlinks the file.
+ </para>
+ <para>
+ <function>pg_dir_ls</> lists all filenames in the named directory.
+ </para>
+
+ <para>
+ The functions shown in <xref
+ linkend="functions-admin-logfile"> allow access to the server
+ logfile, if the stderr log output is redirected.
+ Use of these 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_logfile_rotate</function>()</literal>
+ </entry>
+ <entry><type>int</type></entry>
+ <entry>Trigger logfile rotation</entry>
+ </row>
+ <row>
+ <entry>
+ <literal><function>pg_logdir_ls</function>()</literal>
+ </entry>
+ <entry><type>setof record</type></entry>
+ <entry>lists all logfiles in the pg_log subdirectory</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_logfile_rotate</primary>
+ </indexterm>
+ <para>
+ <function>pg_logfile_rotate</> issues a logfile rotation trigger,
+ which forces the server to close the current logfile and open a
+ fresh one.
+ </para>
+
+ <indexterm zone="functions-admin">
+ <primary>pg_logdir_ls</primary>
+ </indexterm>
+ <para>
+ <function>pg_logdir_ls</> lists all files in the pg_log
+ subdirectory. For each log file, a record consisting of a
+ timestamp of its creation time and a complete path to the file is
+ returned. For convenience, the view pg_logdir_ls wraps the
+ function.
+ </para>
+
</sect1>
</chapter>
Index: src/backend/catalog/system_views.sql
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/catalog/system_views.sql,v
retrieving revision 1.15
diff -u -r1.15 system_views.sql
--- src/backend/catalog/system_views.sql 18 Jun 2005 19:33:42 -0000 1.15
+++ src/backend/catalog/system_views.sql 21 Jun 2005 09:24:33 -0000
@@ -292,3 +292,8 @@
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_logdir_ls AS
+ SELECT *
+ FROM pg_logdir_ls() AS A
+ (filetime timestamp, filename text);
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.454
diff -u -r1.454 postmaster.c
--- src/backend/postmaster/postmaster.c 17 Jun 2005 22:32:44 -0000 1.454
+++ src/backend/postmaster/postmaster.c 21 Jun 2005 09:24:41 -0000
@@ -3319,6 +3319,11 @@
}
}
+ if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+ {
+ kill(SysLoggerPID, SIGUSR1);
+ }
+
PG_SETMASK(&UnBlockSig);
errno = save_errno;
Index: src/backend/postmaster/syslogger.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/syslogger.c,v
retrieving revision 1.15
diff -u -r1.15 syslogger.c
--- src/backend/postmaster/syslogger.c 19 Apr 2005 03:13:59 -0000 1.15
+++ src/backend/postmaster/syslogger.c 21 Jun 2005 09:24:42 -0000
@@ -101,6 +101,7 @@
* 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,6 +118,7 @@
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,7 +202,7 @@
pqsignal(SIGQUIT, SIG_IGN);
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, SIG_IGN);
+ pqsignal(SIGUSR1, sigUsr1Handler); /* request log rotation */
pqsignal(SIGUSR2, SIG_IGN);
/*
@@ -235,7 +237,6 @@
/* main worker loop */
for (;;)
{
- bool rotation_requested = false;
bool time_based_rotation = false;
#ifndef WIN32
@@ -734,6 +735,8 @@
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"
@@ -887,3 +890,10 @@
{
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: /projects/cvsroot/pgsql/src/backend/utils/adt/Makefile,v
retrieving revision 1.57
diff -u -r1.57 Makefile
--- src/backend/utils/adt/Makefile 1 Apr 2004 21:28:45 -0000 1.57
+++ src/backend/utils/adt/Makefile 21 Jun 2005 09:24:42 -0000
@@ -24,7 +24,7 @@
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
+ 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: /projects/cvsroot/pgsql/src/backend/utils/adt/misc.c,v
retrieving revision 1.44
diff -u -r1.44 misc.c
--- src/backend/utils/adt/misc.c 19 Jun 2005 21:34:02 -0000 1.44
+++ src/backend/utils/adt/misc.c 21 Jun 2005 09:24:43 -0000
@@ -17,15 +17,20 @@
#include <sys/file.h>
#include <signal.h>
#include <dirent.h>
+#include <time.h>
+#include <sys/time.h>
#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))
@@ -107,16 +112,170 @@
PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0), SIGINT));
}
-#ifdef NOT_USED
-/* Disabled in 8.0 due to reliability concerns; FIXME someday */
+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);
+}
+
+
+/*
+ * Rotate log file
+ */
+Datum
+pg_logfile_rotate(PG_FUNCTION_ARGS)
+{
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("only superuser can 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);
+}
+
+typedef struct
+{
+ char *location;
+ DIR *dirdesc;
+} directory_fctx;
+
+/*
+ * scan log directory for log files
+ */
+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, sizeof(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_terminate_backend(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM));
}
-#endif
/* Function to find out which databases make use of a tablespace */
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.369
diff -u -r1.369 pg_proc.h
--- src/include/catalog/pg_proc.h 20 Jun 2005 10:29:37 -0000 1.369
+++ src/include/catalog/pg_proc.h 21 Jun 2005 09:25:01 -0000
@@ -3029,6 +3029,41 @@
DESCR("Prepare for taking an online backup");
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 = 2284 ( pg_reload_conf PGNSP PGUID 12 f f t f v 0 23 "" _null_ _null_ _null_ pg_reload_conf -
_null_));
+DESCR("Reloads configuration files");
+
+DATA(insert OID = 2285 ( pg_tablespace_size PGNSP PGUID 12 f f t f v 1 20 "26" _null_ _null_ _null_
pg_tablespace_size- _null_ ));
+DESCR("Calculate total disk space usage for tablespace");
+DATA(insert OID = 2286 ( pg_database_size PGNSP PGUID 12 f f t f v 1 20 "26" _null_ _null_ _null_
pg_database_size- _null_ ));
+DESCR("Calculate total disk space usage for database");
+DATA(insert OID = 2287 ( pg_relation_size PGNSP PGUID 12 f f t f v 1 20 "26" _null_ _null_ _null_
pg_relation_size- _null_ ));
+DESCR("Calculate total disk space usage for relation");
+DATA(insert OID = 2288 ( pg_size_pretty PGNSP PGUID 12 f f t f v 1 25 "20" _null_ _null_ _null_
pg_size_pretty- _null_ ));
+DESCR("Convert a long int to a human readable text using size units");
+
+DATA(insert OID = 1232 ( pg_logfile_rotate PGNSP PGUID 12 f f t f v 0 23 "" _null_ _null_ _null_
pg_logfile_rotate- _null_ ));
+DESCR("rotate log file");
+DATA(insert OID = 1233 ( pg_logdir_ls PGNSP PGUID 12 f f t t v 0 2249 "" _null_ _null_ _null_ pg_logdir_ls
-_null_ ));
+DESCR("list all available log files");
+
+
+DATA(insert OID = 2562 ( pg_file_stat PGNSP PGUID 12 f f t f v 1 2249 "25" _null_ _null_ _null_ pg_file_stat -
_null_));
+DESCR("retrieve file stats");
+DATA(insert OID = 2563 ( pg_file_length PGNSP PGUID 14 f f t f v 1 20 "25" _null_ _null_ _null_ "SELECT len
FROMpg_file_stat($1) AS s(len int8, c timestamp, a timestamp, m timestamp, i bool)" - _null_ ));
+DESCR("returns length of a file");
+DATA(insert OID = 2564 ( pg_file_read PGNSP PGUID 12 f f t f v 3 25 "25 20 20" _null_ _null_ _null_
pg_file_read- _null_ ));
+DESCR("reads text from a file");
+DATA(insert OID = 2565 ( pg_file_write PGNSP PGUID 12 f f t f v 3 20 "25 25 16" _null_ _null_ _null_
pg_file_write- _null_ ));
+DESCR("writes text to a file");
+DATA(insert OID = 2566 ( pg_file_rename PGNSP PGUID 12 f f f f v 3 16 "25 25 25" _null_ _null_ _null_
pg_file_rename- _null_ ));
+DESCR("renames a file and its backup file");
+DATA(insert OID = 2567 ( pg_file_rename PGNSP PGUID 12 f f f t v 2 16 "25 25" _null_ _null_ _null_
pg_file_rename- _null_ ));
+DESCR("renames a file");
+DATA(insert OID = 2568 ( pg_file_unlink PGNSP PGUID 12 f f t f v 1 16 "25" _null_ _null_ _null_ pg_file_unlink
-_null_ ));
+DESCR("unlinks a file");
+DATA(insert OID = 2569 ( pg_dir_ls PGNSP PGUID 12 f f t t v 2 25 "25 16" _null_ _null_ _null_ pg_dir_ls -
_null_));
+DESCR("lists all filenames from a directory");
+
/* Aggregates (moved here from pg_aggregate for 7.3) */
Index: src/include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/storage/pmsignal.h,v
retrieving revision 1.11
diff -u -r1.11 pmsignal.h
--- src/include/storage/pmsignal.h 31 Dec 2004 22:03:42 -0000 1.11
+++ src/include/storage/pmsignal.h 21 Jun 2005 09:25:02 -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/src/include/utils/builtins.h,v
retrieving revision 1.258
diff -u -r1.258 builtins.h
--- src/include/utils/builtins.h 17 Jun 2005 22:32:50 -0000 1.258
+++ src/include/utils/builtins.h 21 Jun 2005 09:25:04 -0000
@@ -357,12 +357,29 @@
extern Datum float84gt(PG_FUNCTION_ARGS);
extern Datum float84ge(PG_FUNCTION_ARGS);
+/* dbsize.c */
+extern Datum pg_tablespace_size(PG_FUNCTION_ARGS);
+extern Datum pg_database_size(PG_FUNCTION_ARGS);
+extern Datum pg_relation_size(PG_FUNCTION_ARGS);
+extern Datum pg_size_pretty(PG_FUNCTION_ARGS);
+
+/* genfile.c */
+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);
+
/* 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_logfile_rotate(PG_FUNCTION_ARGS);
+extern Datum pg_logdir_ls(PG_FUNCTION_ARGS);
/* not_in.c */
extern Datum int4notin(PG_FUNCTION_ARGS);
/*
* dbsize.c
* object size functions
*
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/dbsize/dbsize.c,v 1.17 2005/05/27 00:57:48 neilc Exp $
*
*/
#include "postgres.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "access/heapam.h"
#include "catalog/namespace.h"
#include "catalog/pg_tablespace.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
/* Return physical size of directory contents, or 0 if dir doesn't exist */
static int64
db_dir_size(const char *path)
{
int64 dirsize = 0;
struct dirent *direntry;
DIR *dirdesc;
char filename[MAXPGPATH];
dirdesc = AllocateDir(path);
if (!dirdesc)
return 0;
while ((direntry = readdir(dirdesc)) != NULL)
{
struct stat fst;
if (strcmp(direntry->d_name, ".") == 0 ||
strcmp(direntry->d_name, "..") == 0)
continue;
snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name);
if (stat(filename, &fst) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat \"%s\": %m", filename)));
dirsize += fst.st_size;
}
FreeDir(dirdesc);
return dirsize;
}
static int64
calculate_database_size(Oid dbOid)
{
int64 totalsize = 0;
DIR *dirdesc;
struct dirent *direntry;
char pathname[MAXPGPATH];
/* Shared storage in pg_global is not counted */
/* Include pg_default storage */
snprintf(pathname, MAXPGPATH, "%s/base/%u", DataDir, dbOid);
totalsize += db_dir_size(pathname);
/* Scan the non-default tablespaces */
snprintf(pathname, MAXPGPATH, "%s/pg_tblspc", DataDir);
dirdesc = AllocateDir(pathname);
if (!dirdesc)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open tablespace directory \"%s\": %m",
pathname)));
while ((direntry = readdir(dirdesc)) != NULL)
{
if (strcmp(direntry->d_name, ".") == 0 ||
strcmp(direntry->d_name, "..") == 0)
continue;
snprintf(pathname, MAXPGPATH, "%s/pg_tblspc/%s/%u",
DataDir, direntry->d_name, dbOid);
totalsize += db_dir_size(pathname);
}
FreeDir(dirdesc);
/* Complain if we found no trace of the DB at all */
if (!totalsize)
ereport(ERROR,
(ERRCODE_UNDEFINED_DATABASE,
errmsg("database with OID %u does not exist", dbOid)));
return totalsize;
}
/*
* calculate total size of tablespace
*/
Datum
pg_tablespace_size(PG_FUNCTION_ARGS)
{
Oid tblspcOid = PG_GETARG_OID(0);
char tblspcPath[MAXPGPATH];
char pathname[MAXPGPATH];
int64 totalsize=0;
DIR *dirdesc;
struct dirent *direntry;
if (tblspcOid == DEFAULTTABLESPACE_OID)
snprintf(tblspcPath, MAXPGPATH, "%s/base", DataDir);
else if (tblspcOid == GLOBALTABLESPACE_OID)
snprintf(tblspcPath, MAXPGPATH, "%s/global", DataDir);
else
snprintf(tblspcPath, MAXPGPATH, "%s/pg_tblspc/%u", DataDir, tblspcOid);
dirdesc = AllocateDir(tblspcPath);
if (!dirdesc)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open tablespace directory \"%s\": %m",
tblspcPath)));
while ((direntry = readdir(dirdesc)) != NULL)
{
struct stat fst;
if (strcmp(direntry->d_name, ".") == 0 ||
strcmp(direntry->d_name, "..") == 0)
continue;
snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name);
if (stat(pathname, &fst) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat \"%s\": %m", pathname)));
totalsize += fst.st_size;
if (fst.st_mode & S_IFDIR)
totalsize += db_dir_size(pathname);
}
FreeDir(dirdesc);
PG_RETURN_INT64(totalsize);
}
/*
* calculate size of database in all tablespaces
*/
Datum
pg_database_size(PG_FUNCTION_ARGS)
{
Oid dbOid = PG_GETARG_OID(0);
PG_RETURN_INT64(calculate_database_size(dbOid));
}
/* Calculate relation size given tablespace and relation OIDs */
static int64
calculate_relation_size(Oid tblspcOid, Oid relnodeOid)
{
int64 totalsize=0;
unsigned int segcount=0;
char dirpath[MAXPGPATH];
char pathname[MAXPGPATH];
if (!tblspcOid)
tblspcOid = MyDatabaseTableSpace;
if (tblspcOid == DEFAULTTABLESPACE_OID)
snprintf(dirpath, MAXPGPATH, "%s/base/%u", DataDir, MyDatabaseId);
else if (tblspcOid == GLOBALTABLESPACE_OID)
snprintf(dirpath, MAXPGPATH, "%s/global", DataDir);
else
snprintf(dirpath, MAXPGPATH, "%s/pg_tblspc/%u/%u",
DataDir, tblspcOid, MyDatabaseId);
for (segcount = 0 ;; segcount++)
{
struct stat fst;
if (segcount == 0)
snprintf(pathname, MAXPGPATH, "%s/%u",
dirpath, relnodeOid);
else
snprintf(pathname, MAXPGPATH, "%s/%u.%u",
dirpath, relnodeOid, segcount);
if (stat(pathname, &fst) < 0)
{
if (errno == ENOENT)
break;
else
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat \"%s\": %m", pathname)));
}
totalsize += fst.st_size;
}
return totalsize;
}
/*
* calculate size of relation
*/
Datum
pg_relation_size(PG_FUNCTION_ARGS)
{
Oid relOid=PG_GETARG_OID(0);
HeapTuple tuple;
Form_pg_class pg_class;
Oid relnodeOid;
Oid tblspcOid;
tuple = SearchSysCache(RELOID,
ObjectIdGetDatum(relOid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(ERRCODE_UNDEFINED_TABLE,
errmsg("relation with OID %u does not exist", relOid)));
pg_class = (Form_pg_class) GETSTRUCT(tuple);
relnodeOid = pg_class->relfilenode;
tblspcOid = pg_class->reltablespace;
ReleaseSysCache(tuple);
PG_RETURN_INT64(calculate_relation_size(tblspcOid, relnodeOid));
}
/*
* formatting with size units
*/
Datum
pg_size_pretty(PG_FUNCTION_ARGS)
{
int64 size=PG_GETARG_INT64(0);
char *result=palloc(50+VARHDRSZ);
int64 limit = 10*1024;
int64 mult=1;
if (size < limit*mult)
snprintf(VARDATA(result), 50, INT64_FORMAT" bytes",
size);
else
{
mult *= 1024;
if (size < limit*mult)
snprintf(VARDATA(result), 50, INT64_FORMAT " kB",
(size+mult/2) / mult);
else
{
mult *= 1024;
if (size < limit*mult)
snprintf(VARDATA(result), 50, INT64_FORMAT " MB",
(size+mult/2) / mult);
else
{
mult *= 1024;
if (size < limit*mult)
snprintf(VARDATA(result), 50, INT64_FORMAT " GB",
(size+mult/2) / mult);
else
{
mult *= 1024;
snprintf(VARDATA(result), 50, INT64_FORMAT " TB",
(size+mult/2) / mult);
}
}
}
}
VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ;
PG_RETURN_TEXT_P(result);
}
/*-------------------------------------------------------------------------
*
* 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"
#ifdef WIN32
/* we don't want *nix emulating retry stuff here, but the native behaviour */
#ifdef rename
#undef rename
#endif
#ifdef unlink
#undef unlink
#endif
#endif
extern char *Log_directory;
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, 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)
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);
}