diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index d7738b18b7..ecd17a3528 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -19360,6 +19360,24 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup()); + pg_ls_logdir() + + setof record + + List the name, last modification time and size of files in the log directory. + + + + + pg_ls_waldir() + + setof record + + List the name, last modification time and size of files in the WAL directory. + + + + pg_read_file(filename text [, offset bigint, length bigint [, missing_ok boolean] ]) text @@ -19390,7 +19408,7 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup()); - All of these functions take an optional missing_ok parameter, + Some of these functions take an optional missing_ok parameter, which specifies the behavior when the file or directory does not exist. If true, the function returns NULL (except pg_ls_dir, which returns an empty result set). If diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 38be9cf1a0..4b67102439 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1096,3 +1096,6 @@ REVOKE EXECUTE ON FUNCTION pg_stat_reset() FROM public; REVOKE EXECUTE ON FUNCTION pg_stat_reset_shared(text) FROM public; REVOKE EXECUTE ON FUNCTION pg_stat_reset_single_table_counters(oid) FROM public; REVOKE EXECUTE ON FUNCTION pg_stat_reset_single_function_counters(oid) FROM public; + +REVOKE EXECUTE ON FUNCTION pg_ls_logdir() FROM public; +REVOKE EXECUTE ON FUNCTION pg_ls_waldir() FROM public; diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 66d09bcb0c..a8cdf3bcbf 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -15,12 +15,14 @@ #include "postgres.h" #include +#include #include #include #include #include #include "access/sysattr.h" +#include "access/xlog_internal.h" #include "catalog/pg_authid.h" #include "catalog/catalog.h" #include "catalog/pg_tablespace.h" @@ -46,6 +48,8 @@ #define atooid(x) ((Oid) strtoul((x), NULL, 10)) +/* Generic function to return a directory listing of files */ +Datum pg_ls_dir_files(FunctionCallInfo fcinfo, char *dir); /* * Common subroutine for num_nulls() and num_nonnulls(). @@ -892,3 +896,109 @@ parse_ident(PG_FUNCTION_ARGS) PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext)); } + + +typedef struct +{ + char *location; + DIR *dirdesc; +} directory_fctx; + +/* Generic function to return a directory listing of files */ +Datum +pg_ls_dir_files(FunctionCallInfo fcinfo, char *dir) +{ + FuncCallContext *funcctx; + struct dirent *de; + directory_fctx *fctx; + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc tupdesc; + + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(directory_fctx)); + + tupdesc = CreateTemplateTupleDesc(3, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "mtime", + TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "file", + TEXTOID, -1, 0); + + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + fctx->location = pstrdup(dir); + fctx->dirdesc = AllocateDir(fctx->location); + + if (!fctx->dirdesc) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read directory \"%s\": %m", + fctx->location))); + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + fctx = (directory_fctx *) funcctx->user_fctx; + + while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) + { + char *values[3]; + char *path; + char mtime[25]; + HeapTuple tuple; + struct stat attrib; + + /* + * Skip hidden files + */ + if (de->d_name[0] == '.') + continue; + + /* Get the file info */ + path = psprintf("%s/%s", fctx->location, de->d_name); + stat(path, &attrib); + + /* Ignore anything but regular files */ + if (!S_ISREG(attrib.st_mode)) + continue; + + /* Get the mtime and size */ + strftime(mtime, 25, "%Y-%m-%d %H:%M:%S %Z", localtime(&(attrib.st_ctime))); + const int n = snprintf(NULL, 0, "%lld", attrib.st_size); + char size[n+1]; + snprintf(size, n+1, "%lld", attrib.st_size); + + values[0] = mtime; + values[1] = size; + values[2] = de->d_name; + + tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } + + FreeDir(fctx->dirdesc); + SRF_RETURN_DONE(funcctx); +} + +/* Function to return the list of files in the log directory */ +Datum +pg_ls_logdir(PG_FUNCTION_ARGS) +{ + return pg_ls_dir_files(fcinfo, Log_directory); +} + +/* Function to return the list of files in the WAL directory */ +Datum +pg_ls_waldir(PG_FUNCTION_ARGS) +{ + return pg_ls_dir_files(fcinfo, XLOGDIR); +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index bb7053a942..4ca8044e9a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5361,6 +5361,12 @@ DESCR("pg_controldata init state information as a function"); DATA(insert OID = 3445 ( pg_import_system_collations PGNSP PGUID 12 100 0 0 0 f f f f t f v r 2 0 2278 "16 4089" _null_ _null_ "{if_not_exists,schema}" _null_ _null_ pg_import_system_collations _null_ _null_ _null_ )); DESCR("import collations from operating system"); +/* system management/monitoring related functions */ +DATA(insert OID = 3353 ( pg_ls_logdir PGNSP PGUID 12 10 20 0 0 f f f f t t v s 0 0 2249 "" "{1184,20,25}" "{o,o,o}" "{mtime,size,file}" _null_ _null_ pg_ls_logdir _null_ _null_ _null_ )); +DESCR("list files in the log directory"); +DATA(insert OID = 3354 ( pg_ls_waldir PGNSP PGUID 12 10 20 0 0 f f f f t t v s 0 0 2249 "" "{1184,20,25}" "{o,o,o}" "{mtime,size,file}" _null_ _null_ pg_ls_waldir _null_ _null_ _null_ )); +DESCR("list of files in the WAL directory"); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments,