Skip to content

Commit cb2acb1

Browse files
committed
Add missing_ok option to the SQL functions for reading files.
This makes it possible to use the functions without getting errors, if there is a chance that the file might be removed or renamed concurrently. pg_rewind needs to do just that, although this could be useful for other purposes too. (The changes to pg_rewind to use these functions will come in a separate commit.) The read_binary_file() function isn't very well-suited for extensions.c's purposes anymore, if it ever was. So bite the bullet and make a copy of it in extension.c, tailored for that use case. This seems better than the accidental code reuse, even if it's a some more lines of code. Michael Paquier, with plenty of kibitzing by me.
1 parent cca8ba9 commit cb2acb1

File tree

6 files changed

+272
-100
lines changed

6 files changed

+272
-100
lines changed

doc/src/sgml/func.sgml

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17811,43 +17811,63 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
1781117811
<tbody>
1781217812
<row>
1781317813
<entry>
17814-
<literal><function>pg_ls_dir(<parameter>dirname</> <type>text</>)</function></literal>
17814+
<literal><function>pg_ls_dir(<parameter>dirname</> <type>text</> [, <parameter>missing_ok</> <type>boolean</>, <parameter>include_dot_dirs</> <type>boolean</>])</function></literal>
1781517815
</entry>
1781617816
<entry><type>setof text</type></entry>
17817-
<entry>List the contents of a directory</entry>
17817+
<entry>
17818+
List the contents of a directory.
17819+
</entry>
1781817820
</row>
1781917821
<row>
1782017822
<entry>
17821-
<literal><function>pg_read_file(<parameter>filename</> <type>text</> [, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</>])</function></literal>
17823+
<literal><function>pg_read_file(<parameter>filename</> <type>text</> [, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</> [, <parameter>missing_ok</> <type>boolean</>] ])</function></literal>
1782217824
</entry>
1782317825
<entry><type>text</type></entry>
17824-
<entry>Return the contents of a text file</entry>
17826+
<entry>
17827+
Return the contents of a text file.
17828+
</entry>
1782517829
</row>
1782617830
<row>
1782717831
<entry>
17828-
<literal><function>pg_read_binary_file(<parameter>filename</> <type>text</> [, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</>])</function></literal>
17832+
<literal><function>pg_read_binary_file(<parameter>filename</> <type>text</> [, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</> [, <parameter>missing_ok</> <type>boolean</>] ])</function></literal>
1782917833
</entry>
1783017834
<entry><type>bytea</type></entry>
17831-
<entry>Return the contents of a file</entry>
17835+
<entry>
17836+
Return the contents of a file.
17837+
</entry>
1783217838
</row>
1783317839
<row>
1783417840
<entry>
17835-
<literal><function>pg_stat_file(<parameter>filename</> <type>text</>)</function></literal>
17841+
<literal><function>pg_stat_file(<parameter>filename</> <type>text</>[, <parameter>missing_ok</> <type>boolean</type>])</function></literal>
1783617842
</entry>
1783717843
<entry><type>record</type></entry>
17838-
<entry>Return information about a file</entry>
17844+
<entry>
17845+
Return information about a file.
17846+
</entry>
1783917847
</row>
1784017848
</tbody>
1784117849
</tgroup>
1784217850
</table>
1784317851

17852+
<para>
17853+
All of these functions take an optional <parameter>missing_ok</> parameter,
17854+
which specifies the behaviour when the file or directory does not exist.
17855+
If <literal>true</literal>, the function returns NULL (except
17856+
<function>pg_ls_dir</>, which returns an empty result set). If
17857+
<literal>false</>, an error is raised. The default is <literal>false</>.
17858+
</para>
17859+
1784417860
<indexterm>
1784517861
<primary>pg_ls_dir</primary>
1784617862
</indexterm>
1784717863
<para>
17848-
<function>pg_ls_dir</> returns all the names in the specified
17849-
directory, except the special entries <quote><literal>.</></> and
17850-
<quote><literal>..</></>.
17864+
<function>pg_ls_dir</> returns the names of all files (and directories
17865+
and other special files) in the specified directory. The <parameter>
17866+
include_dot_dirs</> indicates whether <quote>.</> and <quote>..</> are
17867+
included in the result set. The default is to exclude them
17868+
(<literal>false/>), but including them can be useful when
17869+
<parameter>missing_ok</> is <literal>true</literal>, to distinguish an
17870+
empty directory from an non-existent directory.
1785117871
</para>
1785217872

1785317873
<indexterm>

src/backend/commands/extension.c

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#include <dirent.h>
2727
#include <limits.h>
28+
#include <sys/file.h>
29+
#include <sys/stat.h>
2830
#include <unistd.h>
2931

3032
#include "access/htup_details.h"
@@ -51,6 +53,7 @@
5153
#include "utils/builtins.h"
5254
#include "utils/fmgroids.h"
5355
#include "utils/lsyscache.h"
56+
#include "utils/memutils.h"
5457
#include "utils/rel.h"
5558
#include "utils/snapmgr.h"
5659
#include "utils/tqual.h"
@@ -103,6 +106,7 @@ static void ApplyExtensionUpdates(Oid extensionOid,
103106
ExtensionControlFile *pcontrol,
104107
const char *initialVersion,
105108
List *updateVersions);
109+
static char *read_whole_file(const char *filename, int *length);
106110

107111

108112
/*
@@ -635,12 +639,11 @@ read_extension_script_file(const ExtensionControlFile *control,
635639
const char *filename)
636640
{
637641
int src_encoding;
638-
bytea *content;
639642
char *src_str;
640643
char *dest_str;
641644
int len;
642645

643-
content = read_binary_file(filename, 0, -1);
646+
src_str = read_whole_file(filename, &len);
644647

645648
/* use database encoding if not given */
646649
if (control->encoding < 0)
@@ -649,21 +652,15 @@ read_extension_script_file(const ExtensionControlFile *control,
649652
src_encoding = control->encoding;
650653

651654
/* make sure that source string is valid in the expected encoding */
652-
len = VARSIZE_ANY_EXHDR(content);
653-
src_str = VARDATA_ANY(content);
654655
pg_verify_mbstr_len(src_encoding, src_str, len, false);
655656

656-
/* convert the encoding to the database encoding */
657+
/*
658+
* Convert the encoding to the database encoding. read_whole_file
659+
* null-terminated the string, so if no conversion happens the string is
660+
* valid as is.
661+
*/
657662
dest_str = pg_any_to_server(src_str, len, src_encoding);
658663

659-
/* if no conversion happened, we have to arrange for null termination */
660-
if (dest_str == src_str)
661-
{
662-
dest_str = (char *) palloc(len + 1);
663-
memcpy(dest_str, src_str, len);
664-
dest_str[len] = '\0';
665-
}
666-
667664
return dest_str;
668665
}
669666

@@ -3008,3 +3005,49 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
30083005

30093006
return extension;
30103007
}
3008+
3009+
/*
3010+
* Read the whole of file into memory.
3011+
*
3012+
* The file contents are returned as a single palloc'd chunk. For convenience
3013+
* of the callers, an extra \0 byte is added to the end.
3014+
*/
3015+
static char *
3016+
read_whole_file(const char *filename, int *length)
3017+
{
3018+
char *buf;
3019+
FILE *file;
3020+
size_t bytes_to_read;
3021+
struct stat fst;
3022+
3023+
if (stat(filename, &fst) < 0)
3024+
ereport(ERROR,
3025+
(errcode_for_file_access(),
3026+
errmsg("could not stat file \"%s\": %m", filename)));
3027+
3028+
if (fst.st_size > (MaxAllocSize - 1))
3029+
ereport(ERROR,
3030+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3031+
errmsg("file too large")));
3032+
bytes_to_read = (size_t) fst.st_size;
3033+
3034+
if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
3035+
ereport(ERROR,
3036+
(errcode_for_file_access(),
3037+
errmsg("could not open file \"%s\" for reading: %m",
3038+
filename)));
3039+
3040+
buf = (char *) palloc(bytes_to_read + 1);
3041+
3042+
*length = fread(buf, 1, bytes_to_read, file);
3043+
3044+
if (ferror(file))
3045+
ereport(ERROR,
3046+
(errcode_for_file_access(),
3047+
errmsg("could not read file \"%s\": %m", filename)));
3048+
3049+
FreeFile(file);
3050+
3051+
buf[*length] = '\0';
3052+
return buf;
3053+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy