Server instrumentation - Mailing list pgsql-patches
From | Andreas Pflug |
---|---|
Subject | Server instrumentation |
Date | |
Msg-id | 42A95E2D.5010304@pse-consulting.de Whole thread Raw |
Responses |
Re: Server instrumentation
Re: Server instrumentation |
List | pgsql-patches |
This post includes "dbsize backend integration" and "Server instrumentation: pg_terminate_backend, pg_reload_conf" Server instrumentation functions included: pg_tablespace_size() pg_database_size() pg_relation_size() pg_size_pretty() well-known from contrib/dbsize, waiting in unapplied patches http://candle.pha.pa.us/mhonarc/patches/msg00002.html The rest is superuser only: pg_terminate_backend() exposing kill -SIGTERM <backendpid> to the client pg_reload_conf() SIGHUP to reload configuration files pg_logfile_rotate() Logfile rotation pg_logdir_ls() and view pg_logdir_ls lists all available log files in the log directory. As posted in the initial version of the logger subprocess from July 2004. pg_file_stat() pg_file_length() pg_file_read() pg_file_write() pg_file_unlink() pg_file_rename() pg_dir_ls() generic file functions to access files in logdir and cluster dir. These functions where implemented as initiated by Bruce, and discussed intensively in http://archives.postgresql.org/pgsql-patches/2004-07/msg00287.php All functions are fully documented. Additionally, documentation for pg_postmaster_start_time is included, the code itself ist posted by Matthias Schmidt http://candle.pha.pa.us/mhonarc/patches/msg00008.html Regards, Andreas /* * 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); } Index: doc/src/sgml/func.sgml =================================================================== RCS file: /projects/cvsroot/pgsql/doc/src/sgml/func.sgml,v retrieving revision 1.253 diff -u -r1.253 func.sgml --- doc/src/sgml/func.sgml 9 Jun 2005 16:35:08 -0000 1.253 +++ doc/src/sgml/func.sgml 10 Jun 2005 09:27:22 -0000 @@ -8130,6 +8130,11 @@ <entry><type>text</type></entry> <entry>PostgreSQL version information</entry> </row> + <row> + <entry><function>pg_postmaster_start_time()</function></entry> + <entry><type>timestamp</type></entry> + <entry>PostgreSQL postmaster process start time</entry> + </row> </tbody> </tgroup> </table> @@ -8225,6 +8230,15 @@ <productname>PostgreSQL</productname> server's version. </para> + <indexterm zone="functions-info"> + <primary>pg_postmaster_start_time</primary> + </indexterm> + + <para> + <function>pg_postmaster_start_time()</function> returns a timestamp + giving the time when the postmaster process was started. + </para> + <indexterm> <primary>privilege</primary> <secondary>querying</secondary> @@ -8898,6 +8912,12 @@ <indexterm zone="functions-admin"> <primary>pg_cancel_backend</primary> </indexterm> + <indexterm zone="functions-admin"> + <primary>pg_terminate_backend</primary> + </indexterm> + <indexterm zone="functions-admin"> + <primary>pg_reload_conf</primary> + </indexterm> <indexterm zone="functions-admin"> <primary>signal</primary> @@ -8905,7 +8925,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. @@ -8927,21 +8947,46 @@ <entry><type>int</type></entry> <entry>Cancel a backend's current query</entry> </row> + <row> + <entry> + <literal><function>pg_terminate_backend</function>(<parameter>pid</parameter>)</literal> + </entry> + <entry><type>int</type></entry> + <entry>Terminate a backend process</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> + Terminating a backend with <function>pg_terminate_backend</> + should be used only as a last resort, i.e. if the backend process + doesn't react to <function>pg_cancel_backend</> any more and can't + be controlled otherwise. Since the exact state of the + backend at the moment of termination isn't precisely known, some + locked resources might remain in the server's shared memory + structure, effectively blocking other backends. In this case, + you'd have to stop and restart the postmaster. + </para> + <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> @@ -9008,6 +9053,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.13 diff -u -r1.13 system_views.sql --- src/backend/catalog/system_views.sql 17 May 2005 21:46:09 -0000 1.13 +++ src/backend/catalog/system_views.sql 10 Jun 2005 09:27:24 -0000 @@ -282,3 +282,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, filename text); Index: src/backend/postmaster/postmaster.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v retrieving revision 1.452 diff -u -r1.452 postmaster.c --- src/backend/postmaster/postmaster.c 9 Jun 2005 22:01:12 -0000 1.452 +++ src/backend/postmaster/postmaster.c 10 Jun 2005 09:27:31 -0000 @@ -3306,6 +3306,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 10 Jun 2005 09:27:32 -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 10 Jun 2005 09:27:33 -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.43 diff -u -r1.43 misc.c --- src/backend/utils/adt/misc.c 19 May 2005 21:35:47 -0000 1.43 +++ src/backend/utils/adt/misc.c 10 Jun 2005 09:27:33 -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)) @@ -101,22 +106,177 @@ return 1; } + Datum pg_cancel_backend(PG_FUNCTION_ARGS) { 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_terminate_backend(PG_FUNCTION_ARGS) { PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM)); } -#endif + + +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); +} /* 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.365 diff -u -r1.365 pg_proc.h --- src/include/catalog/pg_proc.h 9 Jun 2005 16:35:09 -0000 1.365 +++ src/include/catalog/pg_proc.h 10 Jun 2005 09:27:52 -0000 @@ -3018,12 +3018,50 @@ DESCR("is conversion visible in search path?"); +DATA(insert OID = 2168 ( pg_terminate_backend PGNSP PGUID 12 f f t f v 1 23 "23" _null_ _null_ _null_ pg_terminate_backend- _null_ )); +DESCR("Terminate a server process"); DATA(insert OID = 2171 ( pg_cancel_backend PGNSP PGUID 12 f f t f v 1 23 "23" _null_ _null_ _null_ pg_cancel_backend- _null_ )); DESCR("Cancel a server process' current query"); DATA(insert OID = 2172 ( pg_start_backup PGNSP PGUID 12 f f t f v 1 25 "25" _null_ _null_ _null_ pg_start_backup- _null_ )); 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 = 2560 ( 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 = 2561 ( 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 = 2562 ( 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 = 2563 ( 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 = 2564 ( 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 = 2565 ( 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 = 2566 ( 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 = 2567 ( 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 10 Jun 2005 09:27:53 -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.257 diff -u -r1.257 builtins.h --- src/include/utils/builtins.h 27 May 2005 00:57:49 -0000 1.257 +++ src/include/utils/builtins.h 10 Jun 2005 09:27:55 -0000 @@ -362,7 +362,26 @@ extern Datum nonnullvalue(PG_FUNCTION_ARGS); extern Datum current_database(PG_FUNCTION_ARGS); extern Datum pg_cancel_backend(PG_FUNCTION_ARGS); +extern Datum pg_terminate_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); + +/* 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); /* not_in.c */ extern Datum int4notin(PG_FUNCTION_ARGS);
pgsql-patches by date: