mirror of https://github.com/postgres/postgres
Add pg_file_sync() to adminpack extension.
This function allows us to fsync the specified file or directory. It's useful, for example, when we want to sync the file that pg_file_write() writes out or that COPY TO exports the data into, for durability. Author: Fujii Masao Reviewed-By: Julien Rouhaud, Arthur Zakirov, Michael Paquier, Atsushi Torikoshi Discussion: https://www.postgresql.org/message-id/CAHGQGwGY8uzZ_k8dHRoW1zDcy1Z7=5GQ+So4ZkVy2u=nLsk=hA@mail.gmail.com
This commit is contained in:
parent
cc25464763
commit
d694e0bb79
|
@ -7,7 +7,8 @@ OBJS = \
|
|||
PG_CPPFLAGS = -I$(libpq_srcdir)
|
||||
|
||||
EXTENSION = adminpack
|
||||
DATA = adminpack--1.0.sql adminpack--1.0--1.1.sql adminpack--1.1--2.0.sql
|
||||
DATA = adminpack--1.0.sql adminpack--1.0--1.1.sql adminpack--1.1--2.0.sql\
|
||||
adminpack--2.0--2.1.sql
|
||||
PGFILEDESC = "adminpack - support functions for pgAdmin"
|
||||
|
||||
REGRESS = adminpack
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* contrib/adminpack/adminpack--2.0--2.1.sql */
|
||||
|
||||
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
|
||||
\echo Use "ALTER EXTENSION adminpack UPDATE TO '2.1'" to load this file. \quit
|
||||
|
||||
/* ***********************************************
|
||||
* Administrative functions for PostgreSQL
|
||||
* *********************************************** */
|
||||
|
||||
/* generic file access functions */
|
||||
|
||||
CREATE OR REPLACE FUNCTION pg_catalog.pg_file_sync(text)
|
||||
RETURNS void
|
||||
AS 'MODULE_PATHNAME', 'pg_file_sync'
|
||||
LANGUAGE C VOLATILE STRICT;
|
||||
|
||||
REVOKE EXECUTE ON FUNCTION pg_catalog.pg_file_sync(text) FROM PUBLIC;
|
|
@ -43,6 +43,7 @@ PG_MODULE_MAGIC;
|
|||
|
||||
PG_FUNCTION_INFO_V1(pg_file_write);
|
||||
PG_FUNCTION_INFO_V1(pg_file_write_v1_1);
|
||||
PG_FUNCTION_INFO_V1(pg_file_sync);
|
||||
PG_FUNCTION_INFO_V1(pg_file_rename);
|
||||
PG_FUNCTION_INFO_V1(pg_file_rename_v1_1);
|
||||
PG_FUNCTION_INFO_V1(pg_file_unlink);
|
||||
|
@ -215,6 +216,30 @@ pg_file_write_internal(text *file, text *data, bool replace)
|
|||
return (count);
|
||||
}
|
||||
|
||||
/* ------------------------------------
|
||||
* pg_file_sync
|
||||
*
|
||||
* We REVOKE EXECUTE on the function from PUBLIC.
|
||||
* Users can then grant access to it based on their policies.
|
||||
*/
|
||||
Datum
|
||||
pg_file_sync(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *filename;
|
||||
struct stat fst;
|
||||
|
||||
filename = convert_and_check_filename(PG_GETARG_TEXT_PP(0), false);
|
||||
|
||||
if (stat(filename, &fst) < 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not stat file \"%s\": %m", filename)));
|
||||
|
||||
fsync_fname_ext(filename, S_ISDIR(fst.st_mode), false, ERROR);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/* ------------------------------------
|
||||
* pg_file_rename - old version
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# adminpack extension
|
||||
comment = 'administrative functions for PostgreSQL'
|
||||
default_version = '2.0'
|
||||
default_version = '2.1'
|
||||
module_pathname = '$libdir/adminpack'
|
||||
relocatable = false
|
||||
schema = pg_catalog
|
||||
|
|
|
@ -56,6 +56,21 @@ RESET ROLE;
|
|||
REVOKE EXECUTE ON FUNCTION pg_file_write(text,text,bool) FROM regress_user1;
|
||||
REVOKE pg_read_all_settings FROM regress_user1;
|
||||
DROP ROLE regress_user1;
|
||||
-- sync
|
||||
SELECT pg_file_sync('test_file1'); -- sync file
|
||||
pg_file_sync
|
||||
--------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT pg_file_sync('pg_stat'); -- sync directory
|
||||
pg_file_sync
|
||||
--------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT pg_file_sync('test_file2'); -- not there
|
||||
ERROR: could not stat file "test_file2": No such file or directory
|
||||
-- rename file
|
||||
SELECT pg_file_rename('test_file1', 'test_file2');
|
||||
pg_file_rename
|
||||
|
@ -142,6 +157,8 @@ CREATE USER regress_user1;
|
|||
SET ROLE regress_user1;
|
||||
SELECT pg_file_write('test_file0', 'test0', false);
|
||||
ERROR: permission denied for function pg_file_write
|
||||
SELECT pg_file_sync('test_file0');
|
||||
ERROR: permission denied for function pg_file_sync
|
||||
SELECT pg_file_rename('test_file0', 'test_file0');
|
||||
ERROR: permission denied for function pg_file_rename
|
||||
CONTEXT: SQL function "pg_file_rename" statement 1
|
||||
|
|
|
@ -29,6 +29,11 @@ REVOKE EXECUTE ON FUNCTION pg_file_write(text,text,bool) FROM regress_user1;
|
|||
REVOKE pg_read_all_settings FROM regress_user1;
|
||||
DROP ROLE regress_user1;
|
||||
|
||||
-- sync
|
||||
SELECT pg_file_sync('test_file1'); -- sync file
|
||||
SELECT pg_file_sync('pg_stat'); -- sync directory
|
||||
SELECT pg_file_sync('test_file2'); -- not there
|
||||
|
||||
-- rename file
|
||||
SELECT pg_file_rename('test_file1', 'test_file2');
|
||||
SELECT pg_read_file('test_file1'); -- not there
|
||||
|
@ -58,6 +63,7 @@ CREATE USER regress_user1;
|
|||
SET ROLE regress_user1;
|
||||
|
||||
SELECT pg_file_write('test_file0', 'test0', false);
|
||||
SELECT pg_file_sync('test_file0');
|
||||
SELECT pg_file_rename('test_file0', 'test_file0');
|
||||
SELECT pg_file_unlink('test_file0');
|
||||
SELECT pg_logdir_ls();
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
Write, or append to, a text file
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><function>pg_catalog.pg_file_sync(filename text)</function></entry>
|
||||
<entry><type>void</type></entry>
|
||||
<entry>
|
||||
Flush a file or directory to disk
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><function>pg_catalog.pg_file_rename(oldname text, newname text <optional>, archivename text</optional>)</function></entry>
|
||||
<entry><type>boolean</type></entry>
|
||||
|
@ -79,6 +86,18 @@
|
|||
Returns the number of bytes written.
|
||||
</para>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_file_sync</primary>
|
||||
</indexterm>
|
||||
<para>
|
||||
<function>pg_file_sync</function> fsyncs the specified file or directory
|
||||
named by <parameter>filename</parameter>. An error is thrown
|
||||
on failure (e.g., the specified file is not present). Note that
|
||||
<xref linkend="guc-data-sync-retry"/> has no effect on this function,
|
||||
and therefore a PANIC-level error will not be raised even on failure to
|
||||
flush database files.
|
||||
</para>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_file_rename</primary>
|
||||
</indexterm>
|
||||
|
|
|
@ -319,7 +319,6 @@ static void pre_sync_fname(const char *fname, bool isdir, int elevel);
|
|||
static void datadir_fsync_fname(const char *fname, bool isdir, int elevel);
|
||||
static void unlink_if_exists_fname(const char *fname, bool isdir, int elevel);
|
||||
|
||||
static int fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
|
||||
static int fsync_parent_path(const char *fname, int elevel);
|
||||
|
||||
|
||||
|
@ -3376,7 +3375,7 @@ unlink_if_exists_fname(const char *fname, bool isdir, int elevel)
|
|||
*
|
||||
* Returns 0 if the operation succeeded, -1 otherwise.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel)
|
||||
{
|
||||
int fd;
|
||||
|
|
|
@ -145,6 +145,7 @@ extern int pg_fsync_writethrough(int fd);
|
|||
extern int pg_fdatasync(int fd);
|
||||
extern void pg_flush_data(int fd, off_t offset, off_t amount);
|
||||
extern void fsync_fname(const char *fname, bool isdir);
|
||||
extern int fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
|
||||
extern int durable_rename(const char *oldfile, const char *newfile, int loglevel);
|
||||
extern int durable_unlink(const char *fname, int loglevel);
|
||||
extern int durable_link_or_rename(const char *oldfile, const char *newfile, int loglevel);
|
||||
|
|
Loading…
Reference in New Issue