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.
This commit is contained in:
parent
cca8ba9529
commit
cb2acb1081
@ -17811,43 +17811,63 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
|
|||||||
<tbody>
|
<tbody>
|
||||||
<row>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
<literal><function>pg_ls_dir(<parameter>dirname</> <type>text</>)</function></literal>
|
<literal><function>pg_ls_dir(<parameter>dirname</> <type>text</> [, <parameter>missing_ok</> <type>boolean</>, <parameter>include_dot_dirs</> <type>boolean</>])</function></literal>
|
||||||
</entry>
|
</entry>
|
||||||
<entry><type>setof text</type></entry>
|
<entry><type>setof text</type></entry>
|
||||||
<entry>List the contents of a directory</entry>
|
<entry>
|
||||||
|
List the contents of a directory.
|
||||||
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
<literal><function>pg_read_file(<parameter>filename</> <type>text</> [, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</>])</function></literal>
|
<literal><function>pg_read_file(<parameter>filename</> <type>text</> [, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</> [, <parameter>missing_ok</> <type>boolean</>] ])</function></literal>
|
||||||
</entry>
|
</entry>
|
||||||
<entry><type>text</type></entry>
|
<entry><type>text</type></entry>
|
||||||
<entry>Return the contents of a text file</entry>
|
<entry>
|
||||||
|
Return the contents of a text file.
|
||||||
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
<literal><function>pg_read_binary_file(<parameter>filename</> <type>text</> [, <parameter>offset</> <type>bigint</>, <parameter>length</> <type>bigint</>])</function></literal>
|
<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>
|
||||||
</entry>
|
</entry>
|
||||||
<entry><type>bytea</type></entry>
|
<entry><type>bytea</type></entry>
|
||||||
<entry>Return the contents of a file</entry>
|
<entry>
|
||||||
|
Return the contents of a file.
|
||||||
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
<literal><function>pg_stat_file(<parameter>filename</> <type>text</>)</function></literal>
|
<literal><function>pg_stat_file(<parameter>filename</> <type>text</>[, <parameter>missing_ok</> <type>boolean</type>])</function></literal>
|
||||||
</entry>
|
</entry>
|
||||||
<entry><type>record</type></entry>
|
<entry><type>record</type></entry>
|
||||||
<entry>Return information about a file</entry>
|
<entry>
|
||||||
|
Return information about a file.
|
||||||
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
All of these functions take an optional <parameter>missing_ok</> parameter,
|
||||||
|
which specifies the behaviour when the file or directory does not exist.
|
||||||
|
If <literal>true</literal>, the function returns NULL (except
|
||||||
|
<function>pg_ls_dir</>, which returns an empty result set). If
|
||||||
|
<literal>false</>, an error is raised. The default is <literal>false</>.
|
||||||
|
</para>
|
||||||
|
|
||||||
<indexterm>
|
<indexterm>
|
||||||
<primary>pg_ls_dir</primary>
|
<primary>pg_ls_dir</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
<para>
|
<para>
|
||||||
<function>pg_ls_dir</> returns all the names in the specified
|
<function>pg_ls_dir</> returns the names of all files (and directories
|
||||||
directory, except the special entries <quote><literal>.</></> and
|
and other special files) in the specified directory. The <parameter>
|
||||||
<quote><literal>..</></>.
|
include_dot_dirs</> indicates whether <quote>.</> and <quote>..</> are
|
||||||
|
included in the result set. The default is to exclude them
|
||||||
|
(<literal>false/>), but including them can be useful when
|
||||||
|
<parameter>missing_ok</> is <literal>true</literal>, to distinguish an
|
||||||
|
empty directory from an non-existent directory.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<indexterm>
|
<indexterm>
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
@ -51,6 +53,7 @@
|
|||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
#include "utils/snapmgr.h"
|
#include "utils/snapmgr.h"
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
@ -103,6 +106,7 @@ static void ApplyExtensionUpdates(Oid extensionOid,
|
|||||||
ExtensionControlFile *pcontrol,
|
ExtensionControlFile *pcontrol,
|
||||||
const char *initialVersion,
|
const char *initialVersion,
|
||||||
List *updateVersions);
|
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)
|
const char *filename)
|
||||||
{
|
{
|
||||||
int src_encoding;
|
int src_encoding;
|
||||||
bytea *content;
|
|
||||||
char *src_str;
|
char *src_str;
|
||||||
char *dest_str;
|
char *dest_str;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
content = read_binary_file(filename, 0, -1);
|
src_str = read_whole_file(filename, &len);
|
||||||
|
|
||||||
/* use database encoding if not given */
|
/* use database encoding if not given */
|
||||||
if (control->encoding < 0)
|
if (control->encoding < 0)
|
||||||
@ -649,21 +652,15 @@ read_extension_script_file(const ExtensionControlFile *control,
|
|||||||
src_encoding = control->encoding;
|
src_encoding = control->encoding;
|
||||||
|
|
||||||
/* make sure that source string is valid in the expected 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);
|
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);
|
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;
|
return dest_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3008,3 +3005,49 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
|
|||||||
|
|
||||||
return extension;
|
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;
|
||||||
|
}
|
||||||
|
@ -35,6 +35,7 @@ typedef struct
|
|||||||
{
|
{
|
||||||
char *location;
|
char *location;
|
||||||
DIR *dirdesc;
|
DIR *dirdesc;
|
||||||
|
bool include_dot_dirs;
|
||||||
} directory_fctx;
|
} 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.
|
* We read the whole of the file when bytes_to_read is negative.
|
||||||
*/
|
*/
|
||||||
bytea *
|
static 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 missing_ok)
|
||||||
{
|
{
|
||||||
bytea *buf;
|
bytea *buf;
|
||||||
size_t nbytes;
|
size_t nbytes;
|
||||||
@ -103,9 +105,14 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
|
|||||||
struct stat fst;
|
struct stat fst;
|
||||||
|
|
||||||
if (stat(filename, &fst) < 0)
|
if (stat(filename, &fst) < 0)
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode_for_file_access(),
|
if (missing_ok && errno == ENOENT)
|
||||||
errmsg("could not stat file \"%s\": %m", filename)));
|
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;
|
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")));
|
errmsg("requested length too large")));
|
||||||
|
|
||||||
if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
|
if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode_for_file_access(),
|
if (missing_ok && errno == ENOENT)
|
||||||
errmsg("could not open file \"%s\" for reading: %m",
|
return NULL;
|
||||||
filename)));
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not open file \"%s\" for reading: %m",
|
||||||
|
filename)));
|
||||||
|
}
|
||||||
|
|
||||||
if (fseeko(file, (off_t) seek_offset,
|
if (fseeko(file, (off_t) seek_offset,
|
||||||
(seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0)
|
(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.
|
* in the database encoding.
|
||||||
*/
|
*/
|
||||||
static text *
|
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;
|
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 */
|
if (buf != NULL)
|
||||||
pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
|
{
|
||||||
|
/* Make sure the input is valid */
|
||||||
|
pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
|
||||||
|
|
||||||
/* OK, we can cast it to text safely */
|
/* OK, we can cast it to text safely */
|
||||||
return (text *) buf;
|
return (text *) buf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -170,42 +188,38 @@ Datum
|
|||||||
pg_read_file(PG_FUNCTION_ARGS)
|
pg_read_file(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *filename_t = PG_GETARG_TEXT_P(0);
|
text *filename_t = PG_GETARG_TEXT_P(0);
|
||||||
int64 seek_offset = PG_GETARG_INT64(1);
|
int64 seek_offset = 0;
|
||||||
int64 bytes_to_read = PG_GETARG_INT64(2);
|
int64 bytes_to_read = -1;
|
||||||
|
bool missing_ok = false;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
text *result;
|
||||||
|
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
(errmsg("must be superuser to read files"))));
|
(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)
|
if (bytes_to_read < 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("requested length cannot be negative")));
|
errmsg("requested length cannot be negative")));
|
||||||
|
}
|
||||||
PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
|
if (PG_NARGS() >= 4)
|
||||||
}
|
missing_ok = PG_GETARG_BOOL(3);
|
||||||
|
|
||||||
/*
|
|
||||||
* 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"))));
|
|
||||||
|
|
||||||
filename = convert_and_check_filename(filename_t);
|
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)
|
pg_read_binary_file(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *filename_t = PG_GETARG_TEXT_P(0);
|
text *filename_t = PG_GETARG_TEXT_P(0);
|
||||||
int64 seek_offset = PG_GETARG_INT64(1);
|
int64 seek_offset = 0;
|
||||||
int64 bytes_to_read = PG_GETARG_INT64(2);
|
int64 bytes_to_read = -1;
|
||||||
|
bool missing_ok = false;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
bytea *result;
|
||||||
|
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
(errmsg("must be superuser to read files"))));
|
(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);
|
filename = convert_and_check_filename(filename_t);
|
||||||
|
|
||||||
if (bytes_to_read < 0)
|
result = read_binary_file(filename, seek_offset,
|
||||||
ereport(ERROR,
|
bytes_to_read, missing_ok);
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
if (result)
|
||||||
errmsg("requested length cannot be negative")));
|
PG_RETURN_BYTEA_P(result);
|
||||||
|
else
|
||||||
PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
|
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
|
Datum
|
||||||
pg_read_binary_file_all(PG_FUNCTION_ARGS)
|
pg_read_binary_file_all(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *filename_t = PG_GETARG_TEXT_P(0);
|
return pg_read_binary_file(fcinfo);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -266,18 +310,28 @@ pg_stat_file(PG_FUNCTION_ARGS)
|
|||||||
bool isnull[6];
|
bool isnull[6];
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
|
bool missing_ok = false;
|
||||||
|
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
(errmsg("must be superuser to get file information"))));
|
(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);
|
filename = convert_and_check_filename(filename_t);
|
||||||
|
|
||||||
if (stat(filename, &fst) < 0)
|
if (stat(filename, &fst) < 0)
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode_for_file_access(),
|
if (missing_ok && errno == ENOENT)
|
||||||
errmsg("could not stat file \"%s\": %m", filename)));
|
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
|
* 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));
|
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)
|
* List a directory (returns the filenames only)
|
||||||
@ -330,6 +396,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
|
|||||||
FuncCallContext *funcctx;
|
FuncCallContext *funcctx;
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
directory_fctx *fctx;
|
directory_fctx *fctx;
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -338,7 +405,17 @@ pg_ls_dir(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
if (SRF_IS_FIRSTCALL())
|
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();
|
funcctx = SRF_FIRSTCALL_INIT();
|
||||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||||
@ -346,14 +423,22 @@ pg_ls_dir(PG_FUNCTION_ARGS)
|
|||||||
fctx = palloc(sizeof(directory_fctx));
|
fctx = palloc(sizeof(directory_fctx));
|
||||||
fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
|
fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
|
||||||
|
|
||||||
|
fctx->include_dot_dirs = include_dot_dirs;
|
||||||
fctx->dirdesc = AllocateDir(fctx->location);
|
fctx->dirdesc = AllocateDir(fctx->location);
|
||||||
|
|
||||||
if (!fctx->dirdesc)
|
if (!fctx->dirdesc)
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode_for_file_access(),
|
if (missing_ok && errno == ENOENT)
|
||||||
errmsg("could not open directory \"%s\": %m",
|
{
|
||||||
fctx->location)));
|
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;
|
funcctx->user_fctx = fctx;
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
@ -363,8 +448,9 @@ pg_ls_dir(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
|
while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
|
||||||
{
|
{
|
||||||
if (strcmp(de->d_name, ".") == 0 ||
|
if (!fctx->include_dot_dirs &&
|
||||||
strcmp(de->d_name, "..") == 0)
|
(strcmp(de->d_name, ".") == 0 ||
|
||||||
|
strcmp(de->d_name, "..") == 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
|
SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
|
||||||
@ -374,3 +460,16 @@ pg_ls_dir(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
SRF_RETURN_DONE(funcctx);
|
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);
|
||||||
|
}
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201506111
|
#define CATALOG_VERSION_NO 201506281
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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_ ));
|
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");
|
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");
|
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");
|
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_ ));
|
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");
|
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");
|
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_ ));
|
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");
|
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");
|
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_ ));
|
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");
|
DESCR("sleep for the specified time in seconds");
|
||||||
|
@ -469,14 +469,16 @@ extern Datum pg_filenode_relation(PG_FUNCTION_ARGS);
|
|||||||
extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
|
extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* genfile.c */
|
/* 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(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(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_file_all(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_read_binary_file(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_read_binary_file_all(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
|
extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum pg_ls_dir_1arg(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* misc.c */
|
/* misc.c */
|
||||||
extern Datum current_database(PG_FUNCTION_ARGS);
|
extern Datum current_database(PG_FUNCTION_ARGS);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user