Skip to content

Commit d694e0b

Browse files
committed
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
1 parent cc25464 commit d694e0b

File tree

9 files changed

+89
-4
lines changed

9 files changed

+89
-4
lines changed

contrib/adminpack/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ OBJS = \
77
PG_CPPFLAGS = -I$(libpq_srcdir)
88

99
EXTENSION = adminpack
10-
DATA = adminpack--1.0.sql adminpack--1.0--1.1.sql adminpack--1.1--2.0.sql
10+
DATA = adminpack--1.0.sql adminpack--1.0--1.1.sql adminpack--1.1--2.0.sql\
11+
adminpack--2.0--2.1.sql
1112
PGFILEDESC = "adminpack - support functions for pgAdmin"
1213

1314
REGRESS = adminpack
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* contrib/adminpack/adminpack--2.0--2.1.sql */
2+
3+
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
4+
\echo Use "ALTER EXTENSION adminpack UPDATE TO '2.1'" to load this file. \quit
5+
6+
/* ***********************************************
7+
* Administrative functions for PostgreSQL
8+
* *********************************************** */
9+
10+
/* generic file access functions */
11+
12+
CREATE OR REPLACE FUNCTION pg_catalog.pg_file_sync(text)
13+
RETURNS void
14+
AS 'MODULE_PATHNAME', 'pg_file_sync'
15+
LANGUAGE C VOLATILE STRICT;
16+
17+
REVOKE EXECUTE ON FUNCTION pg_catalog.pg_file_sync(text) FROM PUBLIC;

contrib/adminpack/adminpack.c

+25
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ PG_MODULE_MAGIC;
4343

4444
PG_FUNCTION_INFO_V1(pg_file_write);
4545
PG_FUNCTION_INFO_V1(pg_file_write_v1_1);
46+
PG_FUNCTION_INFO_V1(pg_file_sync);
4647
PG_FUNCTION_INFO_V1(pg_file_rename);
4748
PG_FUNCTION_INFO_V1(pg_file_rename_v1_1);
4849
PG_FUNCTION_INFO_V1(pg_file_unlink);
@@ -215,6 +216,30 @@ pg_file_write_internal(text *file, text *data, bool replace)
215216
return (count);
216217
}
217218

219+
/* ------------------------------------
220+
* pg_file_sync
221+
*
222+
* We REVOKE EXECUTE on the function from PUBLIC.
223+
* Users can then grant access to it based on their policies.
224+
*/
225+
Datum
226+
pg_file_sync(PG_FUNCTION_ARGS)
227+
{
228+
char *filename;
229+
struct stat fst;
230+
231+
filename = convert_and_check_filename(PG_GETARG_TEXT_PP(0), false);
232+
233+
if (stat(filename, &fst) < 0)
234+
ereport(ERROR,
235+
(errcode_for_file_access(),
236+
errmsg("could not stat file \"%s\": %m", filename)));
237+
238+
fsync_fname_ext(filename, S_ISDIR(fst.st_mode), false, ERROR);
239+
240+
PG_RETURN_VOID();
241+
}
242+
218243
/* ------------------------------------
219244
* pg_file_rename - old version
220245
*

contrib/adminpack/adminpack.control

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# adminpack extension
22
comment = 'administrative functions for PostgreSQL'
3-
default_version = '2.0'
3+
default_version = '2.1'
44
module_pathname = '$libdir/adminpack'
55
relocatable = false
66
schema = pg_catalog

contrib/adminpack/expected/adminpack.out

+17
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,21 @@ RESET ROLE;
5656
REVOKE EXECUTE ON FUNCTION pg_file_write(text,text,bool) FROM regress_user1;
5757
REVOKE pg_read_all_settings FROM regress_user1;
5858
DROP ROLE regress_user1;
59+
-- sync
60+
SELECT pg_file_sync('test_file1'); -- sync file
61+
pg_file_sync
62+
--------------
63+
64+
(1 row)
65+
66+
SELECT pg_file_sync('pg_stat'); -- sync directory
67+
pg_file_sync
68+
--------------
69+
70+
(1 row)
71+
72+
SELECT pg_file_sync('test_file2'); -- not there
73+
ERROR: could not stat file "test_file2": No such file or directory
5974
-- rename file
6075
SELECT pg_file_rename('test_file1', 'test_file2');
6176
pg_file_rename
@@ -142,6 +157,8 @@ CREATE USER regress_user1;
142157
SET ROLE regress_user1;
143158
SELECT pg_file_write('test_file0', 'test0', false);
144159
ERROR: permission denied for function pg_file_write
160+
SELECT pg_file_sync('test_file0');
161+
ERROR: permission denied for function pg_file_sync
145162
SELECT pg_file_rename('test_file0', 'test_file0');
146163
ERROR: permission denied for function pg_file_rename
147164
CONTEXT: SQL function "pg_file_rename" statement 1

contrib/adminpack/sql/adminpack.sql

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ REVOKE EXECUTE ON FUNCTION pg_file_write(text,text,bool) FROM regress_user1;
2929
REVOKE pg_read_all_settings FROM regress_user1;
3030
DROP ROLE regress_user1;
3131

32+
-- sync
33+
SELECT pg_file_sync('test_file1'); -- sync file
34+
SELECT pg_file_sync('pg_stat'); -- sync directory
35+
SELECT pg_file_sync('test_file2'); -- not there
36+
3237
-- rename file
3338
SELECT pg_file_rename('test_file1', 'test_file2');
3439
SELECT pg_read_file('test_file1'); -- not there
@@ -58,6 +63,7 @@ CREATE USER regress_user1;
5863
SET ROLE regress_user1;
5964

6065
SELECT pg_file_write('test_file0', 'test0', false);
66+
SELECT pg_file_sync('test_file0');
6167
SELECT pg_file_rename('test_file0', 'test_file0');
6268
SELECT pg_file_unlink('test_file0');
6369
SELECT pg_logdir_ls();

doc/src/sgml/adminpack.sgml

+19
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@
4343
Write, or append to, a text file
4444
</entry>
4545
</row>
46+
<row>
47+
<entry><function>pg_catalog.pg_file_sync(filename text)</function></entry>
48+
<entry><type>void</type></entry>
49+
<entry>
50+
Flush a file or directory to disk
51+
</entry>
52+
</row>
4653
<row>
4754
<entry><function>pg_catalog.pg_file_rename(oldname text, newname text <optional>, archivename text</optional>)</function></entry>
4855
<entry><type>boolean</type></entry>
@@ -79,6 +86,18 @@
7986
Returns the number of bytes written.
8087
</para>
8188

89+
<indexterm>
90+
<primary>pg_file_sync</primary>
91+
</indexterm>
92+
<para>
93+
<function>pg_file_sync</function> fsyncs the specified file or directory
94+
named by <parameter>filename</parameter>. An error is thrown
95+
on failure (e.g., the specified file is not present). Note that
96+
<xref linkend="guc-data-sync-retry"/> has no effect on this function,
97+
and therefore a PANIC-level error will not be raised even on failure to
98+
flush database files.
99+
</para>
100+
82101
<indexterm>
83102
<primary>pg_file_rename</primary>
84103
</indexterm>

src/backend/storage/file/fd.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,6 @@ static void pre_sync_fname(const char *fname, bool isdir, int elevel);
319319
static void datadir_fsync_fname(const char *fname, bool isdir, int elevel);
320320
static void unlink_if_exists_fname(const char *fname, bool isdir, int elevel);
321321

322-
static int fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
323322
static int fsync_parent_path(const char *fname, int elevel);
324323

325324

@@ -3376,7 +3375,7 @@ unlink_if_exists_fname(const char *fname, bool isdir, int elevel)
33763375
*
33773376
* Returns 0 if the operation succeeded, -1 otherwise.
33783377
*/
3379-
static int
3378+
int
33803379
fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel)
33813380
{
33823381
int fd;

src/include/storage/fd.h

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ extern int pg_fsync_writethrough(int fd);
145145
extern int pg_fdatasync(int fd);
146146
extern void pg_flush_data(int fd, off_t offset, off_t amount);
147147
extern void fsync_fname(const char *fname, bool isdir);
148+
extern int fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
148149
extern int durable_rename(const char *oldfile, const char *newfile, int loglevel);
149150
extern int durable_unlink(const char *fname, int loglevel);
150151
extern int durable_link_or_rename(const char *oldfile, const char *newfile, int loglevel);

0 commit comments

Comments
 (0)