diff --git a/contrib/adminpack/Makefile b/contrib/adminpack/Makefile index 9a464b11bc96ad1facc9bf5d9e270021a2ede5b8..630fea7726c7b06bb29a9205376b9c8590a159d9 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 0000000000000000000000000000000000000000..1c6712e81639baaaa0358917153685bea288ad50 --- /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 710f4ea32d69b93df9464eb917aa0377105448ac..7b5a531e080892ed97a58c531afccce2ebb91866 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); @@ -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 * diff --git a/contrib/adminpack/adminpack.control b/contrib/adminpack/adminpack.control index 12569dcdd71a738a052fca6439e9f868ae899eb8..ae35d22156aa65e3848309fd4a41012036033ca2 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 8747ac69a27b51587c29e33a6ee09f398c86ba9a..5738b0f6c4dd071f829055cf679583b44f6f2f70 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 1525f0a82bd84b6f2565c4a3963c9606a3f0a10e..918d0bdc65ee988ded7757b1a25cacbf387feb5e 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 2655417366c184fe0812a170fb5814600278907e..977073f7c8dd2cbb7c9cc857fd5758ff2cd347c1 100644 --- a/doc/src/sgml/adminpack.sgml +++ b/doc/src/sgml/adminpack.sgml @@ -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> diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index fa79b45f631b182f4c0274f279996b57eeeacd4b..b5f4df6a4855172c6695006823a8f572a394bff1 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 c6ce7eacf2a8ccd5943b08bc7165307ae35e60e7..51e2ece3c9df6b96817500147d9059c1d22250ac 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);