Skip to content

Commit dc94f63

Browse files
committed
Use copy_file_range if available to allow COW filesystem optimization.
Use case is for a near instantaneous CREATE DATABASE .. WITH TEMPLATE .. STRATEGY file_copy
1 parent e269479 commit dc94f63

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

src/backend/storage/file/copydir.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,19 @@
1919
#include "postgres.h"
2020

2121
#include <fcntl.h>
22+
#include <sys/stat.h>
2223
#include <unistd.h>
2324

2425
#include "common/file_utils.h"
2526
#include "miscadmin.h"
2627
#include "pgstat.h"
2728
#include "storage/copydir.h"
29+
30+
#include <limits.h>
31+
#include <common/logging.h>
32+
2833
#include "storage/fd.h"
34+
#include "pg_config.h"
2935

3036
/*
3137
* copydir: copy a directory
@@ -71,7 +77,11 @@ copydir(const char *fromdir, const char *todir, bool recurse)
7177
copydir(fromfile, tofile, true);
7278
}
7379
else if (xlde_type == PGFILETYPE_REG)
80+
#if defined(HAVE_COPY_FILE_RANGE)
81+
copy_file_reflink(fromfile, tofile);
82+
#else
7483
copy_file(fromfile, tofile);
84+
#endif
7585
}
7686
FreeDir(xldir);
7787

@@ -214,3 +224,63 @@ copy_file(const char *fromfile, const char *tofile)
214224

215225
pfree(buffer);
216226
}
227+
228+
/*
229+
* copy one file using copy_file_range to give filesystem a chance to do COW optimization
230+
*/
231+
void
232+
copy_file_reflink(const char *fromfile, const char *tofile)
233+
{
234+
#if defined(HAVE_COPY_FILE_RANGE)
235+
int srcfd;
236+
int dstfd;
237+
ssize_t nbytes;
238+
239+
/*
240+
* Open the files
241+
*/
242+
srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
243+
244+
if (srcfd < 0)
245+
ereport(ERROR,
246+
errcode_for_file_access(),
247+
errmsg("could not open file \"%s\": %m", fromfile));
248+
249+
dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
250+
251+
if (dstfd < 0)
252+
ereport(ERROR,
253+
errcode_for_file_access(),
254+
errmsg("could not create file \"%s\": %m", tofile));
255+
256+
/*
257+
* Do the data copying.
258+
*/
259+
do
260+
{
261+
/* If we got a cancel signal during the copy of the file, quit */
262+
CHECK_FOR_INTERRUPTS();
263+
264+
pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
265+
nbytes = copy_file_range(srcfd, NULL, dstfd, NULL, SSIZE_MAX, 0);
266+
pgstat_report_wait_end();
267+
if (nbytes < 0)
268+
ereport(ERROR,
269+
errcode_for_file_access(),
270+
errmsg("could not copy file \"%s\": %m", fromfile));
271+
}
272+
while (nbytes > 0);
273+
274+
if (CloseTransientFile(dstfd) != 0)
275+
ereport(ERROR,
276+
errcode_for_file_access(),
277+
errmsg("could not close file \"%s\": %m", tofile));
278+
279+
if (CloseTransientFile(srcfd) != 0)
280+
ereport(ERROR,
281+
errcode_for_file_access(),
282+
errmsg("could not close file \"%s\": %m", fromfile));
283+
#else
284+
pg_fatal("copy_file_range not available on this platform");
285+
#endif
286+
}

src/include/storage/copydir.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515

1616
extern void copydir(const char *fromdir, const char *todir, bool recurse);
1717
extern void copy_file(const char *fromfile, const char *tofile);
18+
extern void copy_file_reflink(const char *fromfile, const char *tofile);
1819

1920
#endif /* COPYDIR_H */

0 commit comments

Comments
 (0)