diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 80b551e78c..69ac7c0416 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17811,43 +17811,63 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
- pg_ls_dir(dirname> text>)
+ pg_ls_dir(dirname> text> [, missing_ok> boolean>, include_dot_dirs> boolean>])
setof text
- List the contents of a directory
+
+ List the contents of a directory.
+
- pg_read_file(filename> text> [, offset> bigint>, length> bigint>])
+ pg_read_file(filename> text> [, offset> bigint>, length> bigint> [, missing_ok> boolean>] ])
text
- Return the contents of a text file
+
+ Return the contents of a text file.
+
- pg_read_binary_file(filename> text> [, offset> bigint>, length> bigint>])
+ pg_read_binary_file(filename> text> [, offset> bigint>, length> bigint> [, missing_ok> boolean>] ])
bytea
- Return the contents of a file
+
+ Return the contents of a file.
+
- pg_stat_file(filename> text>)
+ pg_stat_file(filename> text>[, missing_ok> boolean])
record
- Return information about a file
+
+ Return information about a file.
+
+
+ All of these functions take an optional missing_ok> parameter,
+ which specifies the behaviour 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
+ false>, an error is raised. The default is false>.
+
+
pg_ls_dir
- pg_ls_dir> returns all the names in the specified
- directory, except the special entries .>> and
- ..>>.
+ pg_ls_dir> returns the names of all files (and directories
+ and other special files) in the specified directory. The
+ include_dot_dirs> indicates whether .> and ..> are
+ included in the result set. The default is to exclude them
+ (false/>), but including them can be useful when
+ missing_ok> is true, to distinguish an
+ empty directory from an non-existent directory.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 5cc74d03c1..2b1dcd0d19 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -25,6 +25,8 @@
#include
#include
+#include
+#include
#include
#include "access/htup_details.h"
@@ -51,6 +53,7 @@
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/tqual.h"
@@ -103,6 +106,7 @@ static void ApplyExtensionUpdates(Oid extensionOid,
ExtensionControlFile *pcontrol,
const char *initialVersion,
List *updateVersions);
+static char *read_whole_file(const char *filename, int *length);
/*
@@ -635,12 +639,11 @@ read_extension_script_file(const ExtensionControlFile *control,
const char *filename)
{
int src_encoding;
- bytea *content;
char *src_str;
char *dest_str;
int len;
- content = read_binary_file(filename, 0, -1);
+ src_str = read_whole_file(filename, &len);
/* use database encoding if not given */
if (control->encoding < 0)
@@ -649,21 +652,15 @@ read_extension_script_file(const ExtensionControlFile *control,
src_encoding = control->encoding;
/* make sure that source string is valid in the expected encoding */
- len = VARSIZE_ANY_EXHDR(content);
- src_str = VARDATA_ANY(content);
pg_verify_mbstr_len(src_encoding, src_str, len, false);
- /* convert the encoding to the database encoding */
+ /*
+ * Convert the encoding to the database encoding. read_whole_file
+ * null-terminated the string, so if no conversion happens the string is
+ * valid as is.
+ */
dest_str = pg_any_to_server(src_str, len, src_encoding);
- /* if no conversion happened, we have to arrange for null termination */
- if (dest_str == src_str)
- {
- dest_str = (char *) palloc(len + 1);
- memcpy(dest_str, src_str, len);
- dest_str[len] = '\0';
- }
-
return dest_str;
}
@@ -3008,3 +3005,49 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
return extension;
}
+
+/*
+ * Read the whole of file into memory.
+ *
+ * The file contents are returned as a single palloc'd chunk. For convenience
+ * of the callers, an extra \0 byte is added to the end.
+ */
+static char *
+read_whole_file(const char *filename, int *length)
+{
+ char *buf;
+ FILE *file;
+ size_t bytes_to_read;
+ struct stat fst;
+
+ if (stat(filename, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+
+ if (fst.st_size > (MaxAllocSize - 1))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("file too large")));
+ bytes_to_read = (size_t) fst.st_size;
+
+ if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ filename)));
+
+ buf = (char *) palloc(bytes_to_read + 1);
+
+ *length = fread(buf, 1, bytes_to_read, file);
+
+ if (ferror(file))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+
+ FreeFile(file);
+
+ buf[*length] = '\0';
+ return buf;
+}
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index f3f3cca515..c4eb10d3cf 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;
@@ -87,8 +88,9 @@ convert_and_check_filename(text *arg)
*
* We read the whole of the file when bytes_to_read is negative.
*/
-bytea *
-read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
+static bytea *
+read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
+ bool missing_ok)
{
bytea *buf;
size_t nbytes;
@@ -103,9 +105,14 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
struct stat fst;
if (stat(filename, &fst) < 0)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not stat file \"%s\": %m", filename)));
+ {
+ if (missing_ok && errno == ENOENT)
+ return NULL;
+ else
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+ }
bytes_to_read = fst.st_size - seek_offset;
}
@@ -118,10 +125,15 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
errmsg("requested length too large")));
if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not open file \"%s\" for reading: %m",
- filename)));
+ {
+ if (missing_ok && errno == ENOENT)
+ return NULL;
+ else
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ filename)));
+ }
if (fseeko(file, (off_t) seek_offset,
(seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
@@ -150,17 +162,23 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
* in the database encoding.
*/
static text *
-read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
+read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read,
+ bool missing_ok)
{
bytea *buf;
- buf = read_binary_file(filename, seek_offset, bytes_to_read);
+ buf = read_binary_file(filename, seek_offset, bytes_to_read, missing_ok);
- /* Make sure the input is valid */
- pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
+ if (buf != NULL)
+ {
+ /* Make sure the input is valid */
+ pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
- /* OK, we can cast it to text safely */
- return (text *) buf;
+ /* OK, we can cast it to text safely */
+ return (text *) buf;
+ }
+ else
+ return NULL;
}
/*
@@ -170,42 +188,38 @@ Datum
pg_read_file(PG_FUNCTION_ARGS)
{
text *filename_t = PG_GETARG_TEXT_P(0);
- int64 seek_offset = PG_GETARG_INT64(1);
- int64 bytes_to_read = PG_GETARG_INT64(2);
+ int64 seek_offset = 0;
+ int64 bytes_to_read = -1;
+ bool missing_ok = false;
char *filename;
+ text *result;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files"))));
- filename = convert_and_check_filename(filename_t);
+ /* handle optional arguments */
+ if (PG_NARGS() >= 3)
+ {
+ seek_offset = PG_GETARG_INT64(1);
+ bytes_to_read = PG_GETARG_INT64(2);
- if (bytes_to_read < 0)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("requested length cannot be negative")));
-
- PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
-}
-
-/*
- * Read the whole of a file, returning it as text
- */
-Datum
-pg_read_file_all(PG_FUNCTION_ARGS)
-{
- text *filename_t = PG_GETARG_TEXT_P(0);
- char *filename;
-
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to read files"))));
+ if (bytes_to_read < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("requested length cannot be negative")));
+ }
+ if (PG_NARGS() >= 4)
+ missing_ok = PG_GETARG_BOOL(3);
filename = convert_and_check_filename(filename_t);
- PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
+ result = read_text_file(filename, seek_offset, bytes_to_read, missing_ok);
+ if (result)
+ PG_RETURN_TEXT_P(result);
+ else
+ PG_RETURN_NULL();
}
/*
@@ -215,42 +229,72 @@ Datum
pg_read_binary_file(PG_FUNCTION_ARGS)
{
text *filename_t = PG_GETARG_TEXT_P(0);
- int64 seek_offset = PG_GETARG_INT64(1);
- int64 bytes_to_read = PG_GETARG_INT64(2);
+ int64 seek_offset = 0;
+ int64 bytes_to_read = -1;
+ bool missing_ok = false;
char *filename;
+ bytea *result;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files"))));
+ /* handle optional arguments */
+ if (PG_NARGS() >= 3)
+ {
+ seek_offset = PG_GETARG_INT64(1);
+ bytes_to_read = PG_GETARG_INT64(2);
+
+ if (bytes_to_read < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("requested length cannot be negative")));
+ }
+ if (PG_NARGS() >= 4)
+ missing_ok = PG_GETARG_BOOL(3);
+
filename = convert_and_check_filename(filename_t);
- if (bytes_to_read < 0)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("requested length cannot be negative")));
-
- PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
+ result = read_binary_file(filename, seek_offset,
+ bytes_to_read, missing_ok);
+ if (result)
+ PG_RETURN_BYTEA_P(result);
+ else
+ PG_RETURN_NULL();
}
+
/*
- * Read the whole of a file, returning it as bytea
+ * Wrapper functions for the 1 and 3 argument variants of pg_read_file()
+ * and pg_binary_read_file().
+ *
+ * These are necessary to pass the sanity check in opr_sanity, which checks
+ * that all built-in functions that share the implementing C function take
+ * the same number of arguments.
*/
+Datum
+pg_read_file_off_len(PG_FUNCTION_ARGS)
+{
+ return pg_read_file(fcinfo);
+}
+
+Datum
+pg_read_file_all(PG_FUNCTION_ARGS)
+{
+ return pg_read_file(fcinfo);
+}
+
+Datum
+pg_read_binary_file_off_len(PG_FUNCTION_ARGS)
+{
+ return pg_read_binary_file(fcinfo);
+}
+
Datum
pg_read_binary_file_all(PG_FUNCTION_ARGS)
{
- text *filename_t = PG_GETARG_TEXT_P(0);
- char *filename;
-
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to read files"))));
-
- filename = convert_and_check_filename(filename_t);
-
- PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
+ return pg_read_binary_file(fcinfo);
}
/*
@@ -266,18 +310,28 @@ pg_stat_file(PG_FUNCTION_ARGS)
bool isnull[6];
HeapTuple tuple;
TupleDesc tupdesc;
+ bool missing_ok = false;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to get file information"))));
+ /* check the optional argument */
+ if (PG_NARGS() == 2)
+ missing_ok = 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 (missing_ok && errno == ENOENT)
+ PG_RETURN_NULL();
+ 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
@@ -320,6 +374,18 @@ pg_stat_file(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
}
+/*
+ * stat a file (1 argument version)
+ *
+ * note: this wrapper is necessary to pass the sanity check in opr_sanity,
+ * which checks that all built-in functions that share the implementing C
+ * function take the same number of arguments
+ */
+Datum
+pg_stat_file_1arg(PG_FUNCTION_ARGS)
+{
+ return pg_stat_file(fcinfo);
+}
/*
* List a directory (returns the filenames only)
@@ -330,6 +396,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
FuncCallContext *funcctx;
struct dirent *de;
directory_fctx *fctx;
+ MemoryContext oldcontext;
if (!superuser())
ereport(ERROR,
@@ -338,7 +405,17 @@ pg_ls_dir(PG_FUNCTION_ARGS)
if (SRF_IS_FIRSTCALL())
{
- MemoryContext oldcontext;
+ bool missing_ok = false;
+ bool include_dot_dirs = false;
+
+ /* check the optional arguments */
+ if (PG_NARGS() == 3)
+ {
+ if (!PG_ARGISNULL(1))
+ missing_ok = 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);
@@ -346,14 +423,22 @@ 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->dirdesc = AllocateDir(fctx->location);
if (!fctx->dirdesc)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not open directory \"%s\": %m",
- fctx->location)));
-
+ {
+ if (missing_ok && errno == ENOENT)
+ {
+ MemoryContextSwitchTo(oldcontext);
+ 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);
}
@@ -363,8 +448,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));
@@ -374,3 +460,16 @@ pg_ls_dir(PG_FUNCTION_ARGS)
SRF_RETURN_DONE(funcctx);
}
+
+/*
+ * List a directory (1 argument version)
+ *
+ * note: this wrapper is necessary to pass the sanity check in opr_sanity,
+ * which checks that all built-in functions that share the implementing C
+ * function take the same number of arguments.
+ */
+Datum
+pg_ls_dir_1arg(PG_FUNCTION_ARGS)
+{
+ return pg_ls_dir(fcinfo);
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index de9018b957..3515b6436a 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201506111
+#define CATALOG_VERSION_NO 201506281
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6b3d1949ef..d1cc8907d7 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3181,17 +3181,25 @@ DESCR("reload configuration files");
DATA(insert OID = 2622 ( pg_rotate_logfile PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
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_ ));
+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_1arg _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_ ));
+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 _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_off_len _null_ _null_ _null_ ));
+DESCR("read text from a file");
+DATA(insert OID = 3293 ( pg_read_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 4 0 25 "25 20 20 16" _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_ ));
DESCR("read text from a file");
-DATA(insert OID = 3827 ( pg_read_binary_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 17 "25 20 20" _null_ _null_ _null_ _null_ _null_ pg_read_binary_file _null_ _null_ _null_ ));
+DATA(insert OID = 3827 ( pg_read_binary_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 17 "25 20 20" _null_ _null_ _null_ _null_ _null_ pg_read_binary_file_off_len _null_ _null_ _null_ ));
+DESCR("read bytea from a file");
+DATA(insert OID = 3295 ( pg_read_binary_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 4 0 17 "25 20 20 16" _null_ _null_ _null_ _null_ _null_ pg_read_binary_file _null_ _null_ _null_ ));
DESCR("read bytea from a file");
DATA(insert OID = 3828 ( pg_read_binary_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 17 "25" _null_ _null_ _null_ _null_ _null_ pg_read_binary_file_all _null_ _null_ _null_ ));
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_ ));
+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_1arg _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 _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");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 51f25a2814..98556725c8 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -469,14 +469,16 @@ extern Datum pg_filenode_relation(PG_FUNCTION_ARGS);
extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
/* genfile.c */
-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_1arg(PG_FUNCTION_ARGS);
extern Datum pg_read_file(PG_FUNCTION_ARGS);
+extern Datum pg_read_file_off_len(PG_FUNCTION_ARGS);
extern Datum pg_read_file_all(PG_FUNCTION_ARGS);
extern Datum pg_read_binary_file(PG_FUNCTION_ARGS);
+extern Datum pg_read_binary_file_off_len(PG_FUNCTION_ARGS);
extern Datum pg_read_binary_file_all(PG_FUNCTION_ARGS);
extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+extern Datum pg_ls_dir_1arg(PG_FUNCTION_ARGS);
/* misc.c */
extern Datum current_database(PG_FUNCTION_ARGS);