diff options
author | Fujii Masao | 2020-01-24 11:42:52 +0000 |
---|---|---|
committer | Fujii Masao | 2020-01-24 11:42:52 +0000 |
commit | d694e0bb793ebd6b392e6ef6a3b0a59ae66cbc95 (patch) | |
tree | e53f68bd8ce500aada11ebd776735d139b572618 | |
parent | cc25464763f0211e59a209eb50a7b7a79449051f (diff) |
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
-rw-r--r-- | contrib/adminpack/Makefile | 3 | ||||
-rw-r--r-- | contrib/adminpack/adminpack--2.0--2.1.sql | 17 | ||||
-rw-r--r-- | contrib/adminpack/adminpack.c | 25 | ||||
-rw-r--r-- | contrib/adminpack/adminpack.control | 2 | ||||
-rw-r--r-- | contrib/adminpack/expected/adminpack.out | 17 | ||||
-rw-r--r-- | contrib/adminpack/sql/adminpack.sql | 6 | ||||
-rw-r--r-- | doc/src/sgml/adminpack.sgml | 19 | ||||
-rw-r--r-- | src/backend/storage/file/fd.c | 3 | ||||
-rw-r--r-- | src/include/storage/fd.h | 1 |
9 files changed, 89 insertions, 4 deletions
diff --git a/contrib/adminpack/Makefile b/contrib/adminpack/Makefile index 9a464b11bc9..630fea7726c 100644 --- a/contrib/adminpack/Makefile +++ b/contrib/adminpack/Makefile @@ -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 diff --git a/contrib/adminpack/adminpack--2.0--2.1.sql b/contrib/adminpack/adminpack--2.0--2.1.sql new file mode 100644 index 00000000000..1c6712e8163 --- /dev/null +++ b/contrib/adminpack/adminpack--2.0--2.1.sql @@ -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; diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c index 710f4ea32d6..7b5a531e080 100644 --- a/contrib/adminpack/adminpack.c +++ b/contrib/adminpack/adminpack.c @@ -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); @@ -216,6 +217,30 @@ pg_file_write_internal(text *file, text *data, bool replace) } /* ------------------------------------ + * 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 * * The superuser() check here must be kept as the library might be upgraded diff --git a/contrib/adminpack/adminpack.control b/contrib/adminpack/adminpack.control index 12569dcdd71..ae35d22156a 100644 --- a/contrib/adminpack/adminpack.control +++ b/contrib/adminpack/adminpack.control @@ -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 diff --git a/contrib/adminpack/expected/adminpack.out b/contrib/adminpack/expected/adminpack.out index 8747ac69a27..5738b0f6c4d 100644 --- a/contrib/adminpack/expected/adminpack.out +++ b/contrib/adminpack/expected/adminpack.out @@ -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 diff --git a/contrib/adminpack/sql/adminpack.sql b/contrib/adminpack/sql/adminpack.sql index 1525f0a82bd..918d0bdc65e 100644 --- a/contrib/adminpack/sql/adminpack.sql +++ b/contrib/adminpack/sql/adminpack.sql @@ -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(); diff --git a/doc/src/sgml/adminpack.sgml b/doc/src/sgml/adminpack.sgml index 2655417366c..977073f7c8d 100644 --- a/doc/src/sgml/adminpack.sgml +++ b/doc/src/sgml/adminpack.sgml @@ -44,6 +44,13 @@ </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> <entry> @@ -80,6 +87,18 @@ </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> <para> diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index fa79b45f631..b5f4df6a485 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -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; diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h index c6ce7eacf2a..51e2ece3c9d 100644 --- a/src/include/storage/fd.h +++ b/src/include/storage/fd.h @@ -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); |