From 0b6b6b438d6a17573f929e50c3adfd726f0b3948 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Wed, 24 Jun 2015 10:37:31 +0900 Subject: [PATCH 3/8] Add IF NOT EXISTS to pg_read_file and pg_read_binary_file This is useful for code paths that do not want to fail if the file queried does not exist. --- doc/src/sgml/func.sgml | 16 +++- src/backend/commands/extension.c | 2 +- src/backend/utils/adt/genfile.c | 174 +++++++++++++++++++++++++++++++++------ src/include/catalog/pg_proc.h | 8 ++ src/include/utils/builtins.h | 8 +- 5 files changed, 179 insertions(+), 29 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 0f70985..edfea8b 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17816,17 +17816,25 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); - pg_read_file(filename text [, offset bigint, length bigint]) + pg_read_file(filename text [, offset bigint, length bigint [, if_not_exists boolean] ]) text - Return the contents of a text file + + Return the contents of a text file. If + if_not_exists is true, NULL + is returned if file does not exist instead of an error. + - pg_read_binary_file(filename text [, offset bigint, length bigint]) + pg_read_binary_file(filename text [, offset bigint, length bigint [, if_not_exists boolean] ]) bytea - Return the contents of a file + + Return the contents of a file. If + if_not_exists is true, NULL + is returned if file does not exist instead of an error. + diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 5cc74d0..25bbe01 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -640,7 +640,7 @@ read_extension_script_file(const ExtensionControlFile *control, char *dest_str; int len; - content = read_binary_file(filename, 0, -1); + content = read_binary_file(filename, 0, -1, false); /* use database encoding if not given */ if (control->encoding < 0) diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index dd97d83..d45c66e 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -88,7 +88,8 @@ 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) +read_binary_file(const char *filename, int64 seek_offset, + int64 bytes_to_read, bool if_not_exists) { bytea *buf; size_t nbytes; @@ -103,9 +104,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 (if_not_exists && 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 +124,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 (if_not_exists && 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,11 +161,16 @@ 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 if_not_exists) { bytea *buf; - buf = read_binary_file(filename, seek_offset, bytes_to_read); + buf = read_binary_file(filename, seek_offset, + bytes_to_read, if_not_exists); + + if (buf == NULL) + return NULL; /* Make sure the input is valid */ pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false); @@ -164,21 +180,27 @@ read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read) } /* - * Read a section of a file, returning it as text + * Wrapper function for pg_read_file and pg_read_file_extended. */ -Datum -pg_read_file(PG_FUNCTION_ARGS) +static Datum +read_file_wrapper(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); + bool if_not_exists = false; char *filename; + bytea *result; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to read files")))); + /* Check for option if_not_exists */ + if (PG_NARGS() == 4 && !PG_ARGISNULL(3)) + if_not_exists = PG_GETARG_BOOL(3); + filename = convert_and_check_filename(filename_t); if (bytes_to_read < 0) @@ -186,44 +208,100 @@ pg_read_file(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested length cannot be negative"))); - PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read)); + result = read_text_file(filename, seek_offset, bytes_to_read, + if_not_exists); + if (result == NULL) + PG_RETURN_NULL(); + PG_RETURN_TEXT_P(result); } /* - * Read the whole of a file, returning it as text + * Read a section of a file, returning it as text */ Datum -pg_read_file_all(PG_FUNCTION_ARGS) +pg_read_file(PG_FUNCTION_ARGS) +{ + return read_file_wrapper(fcinfo); +} + +/* + * Similar to pg_read_file, with IF NOT EXISTS option. + */ +Datum +pg_read_file_extended(PG_FUNCTION_ARGS) +{ + return read_file_wrapper(fcinfo); +} + +/* + * Wrapper function for pg_read_file_all and pg_read_file_all_extended. + */ +static Datum +read_file_all_wrapper(PG_FUNCTION_ARGS) { text *filename_t = PG_GETARG_TEXT_P(0); char *filename; + text *result; + bool if_not_exists = false; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to read files")))); + /* 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); - PG_RETURN_TEXT_P(read_text_file(filename, 0, -1)); + result = read_text_file(filename, 0, -1, if_not_exists); + if (result == NULL) + PG_RETURN_NULL(); + + PG_RETURN_TEXT_P(result); } /* - * Read a section of a file, returning it as bytea + * Read the whole of a file, returning it as text */ Datum -pg_read_binary_file(PG_FUNCTION_ARGS) +pg_read_file_all(PG_FUNCTION_ARGS) +{ + return read_file_all_wrapper(fcinfo); +} + +/* + * Similar to pg_read_file_all, with IF NOT EXISTS option. + */ +Datum +pg_read_file_all_extended(PG_FUNCTION_ARGS) +{ + return read_file_all_wrapper(fcinfo); +} + +/* + * Wrapper for pg_read_binary_file and pg_read_binary_file_extended. + */ +static Datum +read_binary_file_wrapper(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); char *filename; + bool if_not_exists = false; + bytea *result; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to read files")))); + /* Check for option if_not_exists */ + if (PG_NARGS() == 4 && !PG_ARGISNULL(3)) + if_not_exists = PG_GETARG_BOOL(3); + filename = convert_and_check_filename(filename_t); if (bytes_to_read < 0) @@ -231,26 +309,76 @@ pg_read_binary_file(PG_FUNCTION_ARGS) (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, if_not_exists); + if (result == NULL) + PG_RETURN_NULL(); + PG_RETURN_BYTEA_P(result); } /* - * Read the whole of a file, returning it as bytea + * Read a section of a file, returning it as bytea */ Datum -pg_read_binary_file_all(PG_FUNCTION_ARGS) +pg_read_binary_file(PG_FUNCTION_ARGS) +{ + return read_binary_file_wrapper(fcinfo); +} + +/* + * Similar to pg_read_binary_file, with IF NOT EXISTS option. + */ +Datum +pg_read_binary_file_extended(PG_FUNCTION_ARGS) +{ + return read_binary_file_wrapper(fcinfo); +} + +/* + * Wrapper for pg_read_binary_file_all and pg_read_binary_file_all_extended. + */ +static Datum +read_binary_file_all_wrapper(PG_FUNCTION_ARGS) { text *filename_t = PG_GETARG_TEXT_P(0); char *filename; + bool if_not_exists = false; + text *result; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to read files")))); + /* 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); - PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1)); + result = read_binary_file(filename, 0, -1, if_not_exists); + if (result == NULL) + PG_RETURN_NULL(); + + PG_RETURN_BYTEA_P(result); +} + +/* + * Read the whole of a file, returning it as bytea + */ +Datum +pg_read_binary_file_all(PG_FUNCTION_ARGS) +{ + return read_binary_file_all_wrapper(fcinfo); +} + +/* + * Similar to pg_read_binary_file_all, with IF NOT EXISTS option. + */ +Datum +pg_read_binary_file_all_extended(PG_FUNCTION_ARGS) +{ + return read_binary_file_all_wrapper(fcinfo); } /* diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 8da1781..c805a9a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3189,12 +3189,20 @@ DATA(insert OID = 3307 ( pg_stat_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 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 = 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_extended _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 = 3294 ( pg_read_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 25 "25 16" _null_ _null_ _null_ _null_ _null_ pg_read_file_all_extended _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_ )); 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_extended _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 = 3296 ( pg_read_binary_file PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 17 "25 16" _null_ _null_ _null_ _null_ _null_ pg_read_binary_file_all_extended _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_ )); 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_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index e46650a..82d0e18 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -470,13 +470,19 @@ 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); + int64 seek_offset, + int64 bytes_to_read, + bool if_not_exists); 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_extended(PG_FUNCTION_ARGS); extern Datum pg_read_file_all(PG_FUNCTION_ARGS); +extern Datum pg_read_file_all_extended(PG_FUNCTION_ARGS); extern Datum pg_read_binary_file(PG_FUNCTION_ARGS); +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); /* misc.c */ -- 2.4.4