From f40a675db5e086a72e52cc9d0394728a0ada95f8 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Mon, 22 Jun 2015 21:19:33 +0900
Subject: [PATCH 4/6] 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          | 10 +++++--
 src/backend/utils/adt/genfile.c | 59 +++++++++++++++++++++++++++++++++--------
 src/include/catalog/pg_proc.h   |  2 ++
 src/include/utils/builtins.h    |  1 +
 4 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index edfea8b..e9dc19f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17809,10 +17809,16 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
      <tbody>
       <row>
        <entry>
-        <literal><function>pg_ls_dir(<parameter>dirname</> <type>text</>)</function></literal>
+        <literal><function>pg_ls_dir(<parameter>dirname</> <type>text</> [, <parameter>if_not_exists</> <type>boolean</>, <parameter>include_dot_dirs</> <type>boolean</>] )</function></literal>
        </entry>
        <entry><type>setof text</type></entry>
-       <entry>List the contents of a directory</entry>
+       <entry>
+        List the contents of a directory.  If
+        <parameter>if_not_exists</parameter> is true, an empty result is
+        returned instead of an error. If <parameter>include_dot_dirs</>
+        is true, <quote>.</> and <quote>..</> are included as well in
+        the returned list.
+       </entry>
       </row>
       <row>
        <entry>
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 6ce9ec3..6af7253 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -35,6 +35,7 @@ typedef struct
 {
 	char	   *location;
 	DIR		   *dirdesc;
+	bool		include_dot_dirs;
 } directory_fctx;
 
 
@@ -483,14 +484,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 +501,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);
@@ -508,13 +520,18 @@ pg_ls_dir(PG_FUNCTION_ARGS)
 		fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
 
 		fctx->dirdesc = AllocateDir(fctx->location);
+		fctx->include_dot_dirs = include_dot_dirs;
 
 		if (!fctx->dirdesc)
-			ereport(ERROR,
-					(errcode_for_file_access(),
-					 errmsg("could not open directory \"%s\": %m",
-							fctx->location)));
-
+		{
+			if (if_not_exists && errno == ENOENT)
+				SRF_RETURN_DONE(funcctx);
+			else
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not open directory \"%s\": %m",
+								fctx->location)));
+		}
 		funcctx->user_fctx = fctx;
 		MemoryContextSwitchTo(oldcontext);
 	}
@@ -524,8 +541,9 @@ pg_ls_dir(PG_FUNCTION_ARGS)
 
 	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 +553,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

