From 3299e47802759d362a034d718492420e3bea62dd Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Wed, 24 Jun 2015 10:59:16 +0900 Subject: [PATCH 4/8] Extend pg_ls_dir with include_dot_dirs and if_not_exists If include_dot_dirs is set to true, "." and ".." are listed in the list of files listed. If if_not_exists is true, an empty list of files is returned to caller instead of erroring out if the folder specified does not exist. --- doc/src/sgml/func.sgml | 11 +++++-- src/backend/utils/adt/genfile.c | 65 ++++++++++++++++++++++++++++++++++------- src/include/catalog/pg_proc.h | 2 ++ src/include/utils/builtins.h | 1 + 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index edfea8b..3b5161a 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17809,10 +17809,17 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); - pg_ls_dir(dirname text) + pg_ls_dir(dirname text [, if_not_exists boolean, include_dot_dirs boolean] ) setof text - List the contents of a directory + + List the contents of a directory. If + if_not_exists is true, an empty result is + returned instead of an error. If include_dot_dirs + is true, . and .. are included as well in + the returned list, which is useful to distinguish an empty + and an non-existent directory. + diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index d45c66e..12866dd 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -35,6 +35,8 @@ typedef struct { char *location; DIR *dirdesc; + bool include_dot_dirs; + bool leave_early; } directory_fctx; @@ -483,14 +485,15 @@ pg_stat_file(PG_FUNCTION_ARGS) /* - * List a directory (returns the filenames only) + * Wrapper for pg_ls_dir and pg_ls_dir_extended. */ -Datum -pg_ls_dir(PG_FUNCTION_ARGS) +static Datum +ls_dir_wrapper(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; struct dirent *de; directory_fctx *fctx; + MemoryContext oldcontext; if (!superuser()) ereport(ERROR, @@ -499,7 +502,17 @@ pg_ls_dir(PG_FUNCTION_ARGS) if (SRF_IS_FIRSTCALL()) { - MemoryContext oldcontext; + bool if_not_exists = false; + bool include_dot_dirs = false; + + /* see if custom parameters have been passed down */ + if (PG_NARGS() == 3) + { + if (!PG_ARGISNULL(1)) + if_not_exists = PG_GETARG_BOOL(1); + if (!PG_ARGISNULL(2)) + include_dot_dirs = PG_GETARG_BOOL(2); + } funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -507,14 +520,20 @@ pg_ls_dir(PG_FUNCTION_ARGS) fctx = palloc(sizeof(directory_fctx)); fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0)); + fctx->include_dot_dirs = include_dot_dirs; + fctx->leave_early = false; fctx->dirdesc = AllocateDir(fctx->location); if (!fctx->dirdesc) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not open directory \"%s\": %m", - fctx->location))); - + { + if (if_not_exists && errno == ENOENT) + fctx->leave_early = true; + else + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open directory \"%s\": %m", + fctx->location))); + } funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } @@ -522,10 +541,15 @@ pg_ls_dir(PG_FUNCTION_ARGS) funcctx = SRF_PERCALL_SETUP(); fctx = (directory_fctx *) funcctx->user_fctx; + /* Leave earlier if needed */ + if (fctx->leave_early) + SRF_RETURN_DONE(funcctx); + while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) { - if (strcmp(de->d_name, ".") == 0 || - strcmp(de->d_name, "..") == 0) + if (!fctx->include_dot_dirs && + (strcmp(de->d_name, ".") == 0 || + strcmp(de->d_name, "..") == 0)) continue; SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name)); @@ -535,3 +559,22 @@ pg_ls_dir(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } + +/* + * List a directory (returns the filenames only) + */ +Datum +pg_ls_dir(PG_FUNCTION_ARGS) +{ + return ls_dir_wrapper(fcinfo); +} + +/* + * List a directory (can optionally return dot directories or + * use IF NOT EXISTS). + */ +Datum +pg_ls_dir_extended(PG_FUNCTION_ARGS) +{ + return ls_dir_wrapper(fcinfo); +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c805a9a..b559572 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3205,6 +3205,8 @@ DATA(insert OID = 3296 ( pg_read_binary_file PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("read bytea from a file"); DATA(insert OID = 2625 ( pg_ls_dir PGNSP PGUID 12 1 1000 0 0 f f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ )); DESCR("list all files in a directory"); +DATA(insert OID = 3297 ( pg_ls_dir PGNSP PGUID 12 1 1000 0 0 f f f f t t v 3 0 25 "25 16 16" _null_ _null_ _null_ _null_ _null_ pg_ls_dir_extended _null_ _null_ _null_ )); +DESCR("list all files in a directory"); DATA(insert OID = 2626 ( pg_sleep PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ )); DESCR("sleep for the specified time in seconds"); DATA(insert OID = 3935 ( pg_sleep_for PGNSP PGUID 14 1 0 0 0 f f f f t f v 1 0 2278 "1186" _null_ _null_ _null_ _null_ _null_ "select pg_catalog.pg_sleep(extract(epoch from pg_catalog.clock_timestamp() operator(pg_catalog.+) $1) operator(pg_catalog.-) extract(epoch from pg_catalog.clock_timestamp()))" _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 82d0e18..446cda6 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -484,6 +484,7 @@ extern Datum pg_read_binary_file_extended(PG_FUNCTION_ARGS); extern Datum pg_read_binary_file_all(PG_FUNCTION_ARGS); extern Datum pg_read_binary_file_all_extended(PG_FUNCTION_ARGS); extern Datum pg_ls_dir(PG_FUNCTION_ARGS); +extern Datum pg_ls_dir_extended(PG_FUNCTION_ARGS); /* misc.c */ extern Datum current_database(PG_FUNCTION_ARGS); -- 2.4.4