From 5d344fb56cded75e22cb4e56bcf1c10a31f5d4bb Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Date: Thu, 30 Jun 2022 10:30:35 +0900
Subject: [PATCH v3] Add an argument variation to pg_read_file

Let the functions pg_read_file and pg_read_binary_file have the
argument variation of f(filename, missing_ok) so that the functions
can read the whole file tolerating the file to be missing.
---
 doc/src/sgml/func.sgml                   |  4 +--
 src/backend/catalog/system_functions.sql |  4 +++
 src/backend/utils/adt/genfile.c          | 33 +++++++++++++++++++-----
 src/include/catalog/pg_proc.dat          |  6 +++++
 4 files changed, 39 insertions(+), 8 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7b652460a1..34d76b74e4 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -28593,7 +28593,7 @@ SELECT pg_size_pretty(sum(pg_relation_size(relid))) AS total_size
         <indexterm>
          <primary>pg_read_file</primary>
         </indexterm>
-        <function>pg_read_file</function> ( <parameter>filename</parameter> <type>text</type> <optional>, <parameter>offset</parameter> <type>bigint</type>, <parameter>length</parameter> <type>bigint</type> <optional>, <parameter>missing_ok</parameter> <type>boolean</type> </optional></optional> )
+        <function>pg_read_file</function> ( <parameter>filename</parameter> <type>text</type> <optional>, <parameter>offset</parameter> <type>bigint</type>, <parameter>length</parameter> <type>bigint</type> </optional> <optional>, <parameter>missing_ok</parameter> <type>boolean</type> </optional> )
         <returnvalue>text</returnvalue>
        </para>
        <para>
@@ -28618,7 +28618,7 @@ SELECT pg_size_pretty(sum(pg_relation_size(relid))) AS total_size
         <indexterm>
          <primary>pg_read_binary_file</primary>
         </indexterm>
-        <function>pg_read_binary_file</function> ( <parameter>filename</parameter> <type>text</type> <optional>, <parameter>offset</parameter> <type>bigint</type>, <parameter>length</parameter> <type>bigint</type> <optional>, <parameter>missing_ok</parameter> <type>boolean</type> </optional></optional> )
+        <function>pg_read_binary_file</function> ( <parameter>filename</parameter> <type>text</type> <optional>, <parameter>offset</parameter> <type>bigint</type>, <parameter>length</parameter> <type>bigint</type> </optional> <optional>, <parameter>missing_ok</parameter> <type>boolean</type> </optional> )
         <returnvalue>bytea</returnvalue>
        </para>
        <para>
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 73da687d5d..30a048f6b0 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -659,12 +659,16 @@ REVOKE EXECUTE ON FUNCTION pg_ls_tmpdir(oid) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_read_file(text) FROM public;
 
+REVOKE EXECUTE ON FUNCTION pg_read_file(text,boolean) FROM public;
+
 REVOKE EXECUTE ON FUNCTION pg_read_file(text,bigint,bigint) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_read_file(text,bigint,bigint,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_read_binary_file(text) FROM public;
 
+REVOKE EXECUTE ON FUNCTION pg_read_binary_file(text,boolean) FROM public;
+
 REVOKE EXECUTE ON FUNCTION pg_read_binary_file(text,bigint,bigint) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_read_binary_file(text,bigint,bigint,boolean) FROM public;
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 2bf5219256..1a09431a79 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -278,6 +278,9 @@ pg_read_file(PG_FUNCTION_ARGS)
  *
  * No superuser check done here- instead privileges are handled by the
  * GRANT system.
+ *
+ * The two-argument variation of this function supposes the second argument as
+ * to be a Boolean.
  */
 Datum
 pg_read_file_v2(PG_FUNCTION_ARGS)
@@ -290,7 +293,9 @@ pg_read_file_v2(PG_FUNCTION_ARGS)
 	text	   *result;
 
 	/* handle optional arguments */
-	if (PG_NARGS() >= 3)
+	if (PG_NARGS() == 2)
+		missing_ok = PG_GETARG_BOOL(1);
+	else if (PG_NARGS() >= 3)
 	{
 		seek_offset = PG_GETARG_INT64(1);
 		bytes_to_read = PG_GETARG_INT64(2);
@@ -299,9 +304,10 @@ pg_read_file_v2(PG_FUNCTION_ARGS)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("requested length cannot be negative")));
+
+		if (PG_NARGS() >= 4)
+			missing_ok = PG_GETARG_BOOL(3);
 	}
-	if (PG_NARGS() >= 4)
-		missing_ok = PG_GETARG_BOOL(3);
 
 	filename = convert_and_check_filename(filename_t);
 
@@ -326,6 +332,8 @@ pg_read_binary_file(PG_FUNCTION_ARGS)
 	bytea	   *result;
 
 	/* handle optional arguments */
+	if (PG_NARGS() == 2)
+		missing_ok = PG_GETARG_BOOL(1);
 	if (PG_NARGS() >= 3)
 	{
 		seek_offset = PG_GETARG_INT64(1);
@@ -335,9 +343,10 @@ pg_read_binary_file(PG_FUNCTION_ARGS)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("requested length cannot be negative")));
+
+		if (PG_NARGS() >= 4)
+			missing_ok = PG_GETARG_BOOL(3);
 	}
-	if (PG_NARGS() >= 4)
-		missing_ok = PG_GETARG_BOOL(3);
 
 	filename = convert_and_check_filename(filename_t);
 
@@ -351,7 +360,7 @@ pg_read_binary_file(PG_FUNCTION_ARGS)
 
 
 /*
- * Wrapper functions for the 1 and 3 argument variants of pg_read_file_v2()
+ * Wrapper functions for the 1 to 3 argument variants of pg_read_file_v2()
  * and pg_read_binary_file().
  *
  * These are necessary to pass the sanity check in opr_sanity, which checks
@@ -364,6 +373,12 @@ pg_read_file_off_len(PG_FUNCTION_ARGS)
 	return pg_read_file_v2(fcinfo);
 }
 
+Datum
+pg_read_file_all_missing(PG_FUNCTION_ARGS)
+{
+	return pg_read_file_v2(fcinfo);
+}
+
 Datum
 pg_read_file_all(PG_FUNCTION_ARGS)
 {
@@ -376,6 +391,12 @@ pg_read_binary_file_off_len(PG_FUNCTION_ARGS)
 	return pg_read_binary_file(fcinfo);
 }
 
+Datum
+pg_read_binary_file_all_missing(PG_FUNCTION_ARGS)
+{
+	return pg_read_binary_file(fcinfo);
+}
+
 Datum
 pg_read_binary_file_all(PG_FUNCTION_ARGS)
 {
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87aa571a33..84be83610a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6423,6 +6423,9 @@
 { oid => '3826', descr => 'read text from a file',
   proname => 'pg_read_file', provolatile => 'v', prorettype => 'text',
   proargtypes => 'text', prosrc => 'pg_read_file_all' },
+{ oid => '8025', descr => 'read text from a file',
+  proname => 'pg_read_file', provolatile => 'v', prorettype => 'text',
+  proargtypes => 'text bool', prosrc => 'pg_read_file_all_missing' },
 { oid => '3827', descr => 'read bytea from a file',
   proname => 'pg_read_binary_file', provolatile => 'v', prorettype => 'bytea',
   proargtypes => 'text int8 int8', prosrc => 'pg_read_binary_file_off_len' },
@@ -6432,6 +6435,9 @@
 { oid => '3828', descr => 'read bytea from a file',
   proname => 'pg_read_binary_file', provolatile => 'v', prorettype => 'bytea',
   proargtypes => 'text', prosrc => 'pg_read_binary_file_all' },
+{ oid => '8026', descr => 'read bytea from a file',
+  proname => 'pg_read_binary_file', provolatile => 'v', prorettype => 'bytea',
+  proargtypes => 'text bool', prosrc => 'pg_read_binary_file_all_missing' },
 { oid => '2625', descr => 'list all files in a directory',
   proname => 'pg_ls_dir', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'text', proargtypes => 'text',
-- 
2.31.1

