diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 21f1ddf..7c1ba9d 100644 *** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** postgres=# SELECT * FROM pg_xlogfile_nam *** 14449,14461 **** ! pg_read_file(filename text, offset bigint, length bigint) text Return the contents of a text file pg_stat_file(filename text) record --- 14449,14468 ---- ! pg_read_file(filename text [, offset bigint, length bigint]) text Return the contents of a text file + pg_read_binary_file(filename text [, offset bigint, length bigint]) + + bytea + Return the contents of a file + + + pg_stat_file(filename text) record *************** postgres=# SELECT * FROM pg_xlogfile_nam *** 14482,14487 **** --- 14489,14510 ---- at the given offset, returning at most length bytes (less if the end of file is reached first). If offset is negative, it is relative to the end of the file. + When offset and length parameters are omitted, + it returns the whole of the file. + The part of a file must be a valid text in the server encoding. + + + + pg_read_binary_file + + + pg_read_binary_file returns part of a file as like as + pg_read_file, but the result is a bytea value. + One of the usages is to read a file in the specified encoding combined with + convert_from function: + + SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8'); + diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index e8a36ed..e921250 100644 *** a/src/backend/utils/adt/genfile.c --- b/src/backend/utils/adt/genfile.c *************** convert_and_check_filename(text *arg) *** 80,94 **** /* ! * Read a section of a file, returning it as text */ ! 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); ! char *buf; size_t nbytes; FILE *file; char *filename; --- 80,93 ---- /* ! * Read a section of a file, returning it as bytea ! * ! * We read the whole of the file when bytes_to_read is nagative. */ ! static bytea * ! read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read) { ! bytea *buf; size_t nbytes; FILE *file; char *filename; *************** pg_read_file(PG_FUNCTION_ARGS) *** 100,105 **** --- 99,127 ---- filename = convert_and_check_filename(filename_t); + if (bytes_to_read < 0) + { + if (seek_offset < 0) + bytes_to_read = -seek_offset; + else + { + struct stat fst; + + if (stat(filename, &fst) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat file \"%s\": %m", filename))); + + bytes_to_read = fst.st_size - seek_offset; + } + } + + /* not sure why anyone thought that int64 length was a good idea */ + if (bytes_to_read > (MaxAllocSize - VARHDRSZ)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("requested length too large"))); + if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) ereport(ERROR, (errcode_for_file_access(), *************** pg_read_file(PG_FUNCTION_ARGS) *** 112,129 **** (errcode_for_file_access(), errmsg("could not seek in file \"%s\": %m", filename))); ! if (bytes_to_read < 0) ! ereport(ERROR, ! (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ! errmsg("requested length cannot be negative"))); ! ! /* not sure why anyone thought that int64 length was a good idea */ ! if (bytes_to_read > (MaxAllocSize - VARHDRSZ)) ! ereport(ERROR, ! (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ! errmsg("requested length too large"))); ! ! buf = palloc((Size) bytes_to_read + VARHDRSZ); nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file); --- 134,140 ---- (errcode_for_file_access(), errmsg("could not seek in file \"%s\": %m", filename))); ! buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ); nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file); *************** pg_read_file(PG_FUNCTION_ARGS) *** 132,146 **** (errcode_for_file_access(), errmsg("could not read file \"%s\": %m", filename))); - /* Make sure the input is valid */ - pg_verifymbstr(VARDATA(buf), nbytes, false); - SET_VARSIZE(buf, nbytes + VARHDRSZ); FreeFile(file); pfree(filename); ! PG_RETURN_TEXT_P(buf); } /* --- 143,228 ---- (errcode_for_file_access(), errmsg("could not read file \"%s\": %m", filename))); SET_VARSIZE(buf, nbytes + VARHDRSZ); FreeFile(file); pfree(filename); ! return buf; ! } ! ! /* ! * In addition to read_binary_file, verify whether the contents are encoded ! * in the database encoding. ! */ ! static text * ! read_text_file(text *filename, int64 seek_offset, int64 bytes_to_read) ! { ! bytea *buf = read_binary_file(filename, seek_offset, bytes_to_read); ! ! /* Make sure the input is valid */ ! pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false); ! ! /* OK, we can cast it as text safely */ ! return (text *) buf; ! } ! ! /* ! * Read a section of a file, returning it as text ! */ ! 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); ! ! 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_t, 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); ! ! PG_RETURN_TEXT_P(read_text_file(filename_t, 0, -1)); ! } ! ! /* ! * Read a section of a file, returning it as bytea ! */ ! 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); ! ! 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_t, seek_offset, bytes_to_read)); ! } ! ! /* ! * Read the whole of a file, returning it as bytea ! */ ! Datum ! pg_read_binary_file_all(PG_FUNCTION_ARGS) ! { ! text *filename_t = PG_GETARG_TEXT_P(0); ! ! PG_RETURN_BYTEA_P(read_binary_file(filename_t, 0, -1)); } /* diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index feae22e..1e6e75f 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DATA(insert OID = 2623 ( pg_stat_file P *** 3403,3408 **** --- 3403,3414 ---- DESCR("return file information"); DATA(insert OID = 2624 ( pg_read_file PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _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 f f f t f v 1 0 25 "25" _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 f f f t f v 3 0 17 "25 20 20" _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 f f f t f v 1 0 17 "25" _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 f f f t t v 1 0 25 "25" _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 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index a2fb749..1888e31 100644 *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** extern Datum pg_relation_filepath(PG_FUN *** 442,447 **** --- 442,450 ---- /* genfile.c */ extern Datum pg_stat_file(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); + extern Datum pg_read_binary_file_all(PG_FUNCTION_ARGS); extern Datum pg_ls_dir(PG_FUNCTION_ARGS); /* misc.c */