From 72ef91958e91e9d6ba4a72d325872edb65166cc7 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Tue, 23 Jun 2015 11:48:34 +0900
Subject: [PATCH 2/6] Extend pg_stat_file with if_not_exists option

This is useful for code paths that prefer receiving a NULL result
instead of an ERROR if file specified does not exist.
---
 doc/src/sgml/func.sgml          |  9 ++++--
 src/backend/utils/adt/genfile.c | 65 +++++++++++++++++++++++++++++++----------
 src/include/catalog/pg_proc.h   |  2 ++
 src/include/utils/builtins.h    |  1 +
 4 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7929e7e..0f70985 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17830,10 +17830,15 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
       </row>
       <row>
        <entry>
-        <literal><function>pg_stat_file(<parameter>filename</> <type>text</>)</function></literal>
+        <literal><function>pg_stat_file(<parameter>filename</> <type>text</>[, <parameter>if_not_exists</> <type>boolean</type>])</function></literal>
        </entry>
        <entry><type>record</type></entry>
-       <entry>Return information about a file</entry>
+       <entry>
+        Return information about a file. If
+        <parameter>if_not_exists</parameter> is true, <literal>NULL</literal>
+        is returned for all result fields if file does not exist instead of
+        an error.
+       </entry>
       </row>
      </tbody>
     </tgroup>
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index f3f3cca..93eb379 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -254,10 +254,10 @@ pg_read_binary_file_all(PG_FUNCTION_ARGS)
 }
 
 /*
- * stat a file
+ * Wrapper for pg_stat_file and pg_stat_file_extended.
  */
-Datum
-pg_stat_file(PG_FUNCTION_ARGS)
+static Datum
+stat_file_wrapper(PG_FUNCTION_ARGS)
 {
 	text	   *filename_t = PG_GETARG_TEXT_P(0);
 	char	   *filename;
@@ -266,18 +266,29 @@ pg_stat_file(PG_FUNCTION_ARGS)
 	bool		isnull[6];
 	HeapTuple	tuple;
 	TupleDesc	tupdesc;
+	bool		if_not_exists = false;
+	bool		return_null = false;
 
 	if (!superuser())
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 (errmsg("must be superuser to get file information"))));
 
+	/* Check for option if_not_exists */
+	if (PG_NARGS() == 2 && !PG_ARGISNULL(1))
+		if_not_exists = PG_GETARG_BOOL(1);
+
 	filename = convert_and_check_filename(filename_t);
 
 	if (stat(filename, &fst) < 0)
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not stat file \"%s\": %m", filename)));
+	{
+		if (if_not_exists)
+			return_null = true;
+		else
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not stat file \"%s\": %m", filename)));
+	}
 
 	/*
 	 * This record type had better match the output parameters declared for me
@@ -298,20 +309,23 @@ pg_stat_file(PG_FUNCTION_ARGS)
 					   "isdir", BOOLOID, -1, 0);
 	BlessTupleDesc(tupdesc);
 
-	memset(isnull, false, sizeof(isnull));
+	memset(isnull, return_null, sizeof(isnull));
 
-	values[0] = Int64GetDatum((int64) fst.st_size);
-	values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
-	values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
-	/* Unix has file status change time, while Win32 has creation time */
+	if (!return_null)
+	{
+		values[0] = Int64GetDatum((int64) fst.st_size);
+		values[1] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_atime));
+		values[2] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_mtime));
+		/* Unix has file status change time, while Win32 has creation time */
 #if !defined(WIN32) && !defined(__CYGWIN__)
-	values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
-	isnull[4] = true;
+		values[3] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
+		isnull[4] = true;
 #else
-	isnull[3] = true;
-	values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
+		isnull[3] = true;
+		values[4] = TimestampTzGetDatum(time_t_to_timestamptz(fst.st_ctime));
 #endif
-	values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
+		values[5] = BoolGetDatum(S_ISDIR(fst.st_mode));
+	}
 
 	tuple = heap_form_tuple(tupdesc, values, isnull);
 
@@ -320,6 +334,25 @@ pg_stat_file(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
 }
 
+/*
+ * pg_stat_file_extended
+ * Stat a file, with possibility to bypass error in case of a missing file.
+ */
+Datum
+pg_stat_file_extended(PG_FUNCTION_ARGS)
+{
+	return stat_file_wrapper(fcinfo);
+}
+
+/*
+ * stat a file
+ */
+Datum
+pg_stat_file(PG_FUNCTION_ARGS)
+{
+	return stat_file_wrapper(fcinfo);
+}
+
 
 /*
  * List a directory (returns the filenames only)
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 1f163f0..8da1781 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3185,6 +3185,8 @@ DESCR("rotate log file");
 
 DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ _null_ pg_stat_file _null_ _null_ _null_ ));
 DESCR("get information about file");
+DATA(insert OID = 3307 ( pg_stat_file		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2249 "25 16" "{25,16,20,1184,1184,1184,1184,16}" "{i,i,o,o,o,o,o,o}" "{filename,if_not_exists,size,access,modification,change,creation,isdir}" _null_ _null_ pg_stat_file_extended _null_ _null_ _null_ ));
+DESCR("get information about file");
 DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
 DESCR("read text from a file");
 DATA(insert OID = 3826 ( pg_read_file		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ pg_read_file_all _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index ab78ec2..e46650a 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -472,6 +472,7 @@ extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
 extern bytea *read_binary_file(const char *filename,
 				 int64 seek_offset, int64 bytes_to_read);
 extern Datum pg_stat_file(PG_FUNCTION_ARGS);
+extern Datum pg_stat_file_extended(PG_FUNCTION_ARGS);
 extern Datum pg_read_file(PG_FUNCTION_ARGS);
 extern Datum pg_read_file_all(PG_FUNCTION_ARGS);
 extern Datum pg_read_binary_file(PG_FUNCTION_ARGS);
-- 
2.4.4

