Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions src/backend/commands/dbcommands.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ typedef enum CreateDBStrategy
{
CREATEDB_WAL_LOG,
CREATEDB_FILE_COPY,
CREATEDB_COPY_FILE_BY_RANGE,
} CreateDBStrategy;

typedef struct
Expand Down Expand Up @@ -136,7 +137,8 @@ static CreateDBRelInfo *ScanSourceDatabasePgClassTuple(HeapTupleData *tuple,
static void CreateDirAndVersionFile(char *dbpath, Oid dbid, Oid tsid,
bool isRedo);
static void CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid,
Oid src_tsid, Oid dst_tsid);
Oid src_tsid, Oid dst_tsid,
CopyMethod copy_method);
static void recovery_create_dbdir(char *path, bool only_tblspc);

/*
Expand Down Expand Up @@ -548,7 +550,7 @@ CreateDirAndVersionFile(char *dbpath, Oid dbid, Oid tsid, bool isRedo)
*/
static void
CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
Oid dst_tsid)
Oid dst_tsid, CopyMethod copy_method)
{
TableScanDesc scan;
Relation rel;
Expand Down Expand Up @@ -608,7 +610,7 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
*
* We don't need to copy subdirectories
*/
copydir(srcpath, dstpath, false);
copydir_with_method(srcpath, dstpath, false, copy_method);

/* Record the filesystem change in XLOG */
{
Expand Down Expand Up @@ -1022,6 +1024,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
dbstrategy = CREATEDB_WAL_LOG;
else if (pg_strcasecmp(strategy, "file_copy") == 0)
dbstrategy = CREATEDB_FILE_COPY;
else if (pg_strcasecmp(strategy, "copy_file_range") == 0)
dbstrategy = CREATEDB_COPY_FILE_BY_RANGE;
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
Expand Down Expand Up @@ -1508,9 +1512,12 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
if (dbstrategy == CREATEDB_WAL_LOG)
CreateDatabaseUsingWalLog(src_dboid, dboid, src_deftablespace,
dst_deftablespace);
else if (dbstrategy == CREATEDB_COPY_FILE_BY_RANGE)
CreateDatabaseUsingFileCopy(src_dboid, dboid, src_deftablespace,
dst_deftablespace, COPY_METHOD_COPY_FILE_RANGE);
else
CreateDatabaseUsingFileCopy(src_dboid, dboid, src_deftablespace,
dst_deftablespace);
dst_deftablespace, COPY_METHOD_COPY);

/*
* Close pg_database, but keep lock till commit.
Expand Down
81 changes: 79 additions & 2 deletions src/backend/storage/file/copydir.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "pgstat.h"
#include "storage/copydir.h"
#include "storage/fd.h"
#include "pg_config.h"

/*
* copydir: copy a directory
Expand All @@ -35,6 +36,17 @@
*/
void
copydir(const char *fromdir, const char *todir, bool recurse)
{
copydir_with_method(fromdir, todir, recurse, COPY_METHOD_COPY);
}

/*
* copydir_with_method: copy a directory using a specific copy method
*
* Method is either a simple copy or a call to copy_file_range.
*/
void
copydir_with_method(const char *fromdir, const char *todir, bool recurse, CopyMethod copy_method)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just renamed and made a wrapper so that the other places copydir is called isn't affected

{
DIR *xldir;
struct dirent *xlde;
Expand Down Expand Up @@ -68,10 +80,15 @@ copydir(const char *fromdir, const char *todir, bool recurse)
{
/* recurse to handle subdirectories */
if (recurse)
copydir(fromfile, tofile, true);
copydir_with_method(fromfile, tofile, true, copy_method);
}
else if (xlde_type == PGFILETYPE_REG)
copy_file(fromfile, tofile);
{
if (copy_method == COPY_METHOD_COPY_FILE_RANGE)
copy_file_by_range(fromfile, tofile);
else
copy_file(fromfile, tofile);
}
}
FreeDir(xldir);

Expand Down Expand Up @@ -214,3 +231,63 @@ copy_file(const char *fromfile, const char *tofile)

pfree(buffer);
}

/*
* copy one file using copy_file_range to give filesystem a chance to do COW optimization
*/
void
copy_file_by_range(const char *fromfile, const char *tofile)
{
#if defined(HAVE_COPY_FILE_RANGE)
int srcfd;
int dstfd;
ssize_t nbytes;

/*
* Open the files
*/
srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);

if (srcfd < 0)
ereport(ERROR,
errcode_for_file_access(),
errmsg("could not open file \"%s\": %m", fromfile));

dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);

if (dstfd < 0)
ereport(ERROR,
errcode_for_file_access(),
errmsg("could not create file \"%s\": %m", tofile));

/*
* Do the data copying.
*/
do
{
/* If we got a cancel signal during the copy of the file, quit */
CHECK_FOR_INTERRUPTS();

pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
nbytes = copy_file_range(srcfd, NULL, dstfd, NULL, SSIZE_MAX, 0);
pgstat_report_wait_end();
if (nbytes < 0)
ereport(ERROR,
errcode_for_file_access(),
errmsg("could not copy file \"%s\": %m", fromfile));
}
while (nbytes > 0);

if (CloseTransientFile(dstfd) != 0)
ereport(ERROR,
errcode_for_file_access(),
errmsg("could not close file \"%s\": %m", tofile));

if (CloseTransientFile(srcfd) != 0)
ereport(ERROR,
errcode_for_file_access(),
errmsg("could not close file \"%s\": %m", fromfile));
#else
pg_fatal("copy_file_range not available on this platform");
#endif
}
11 changes: 11 additions & 0 deletions src/include/storage/copydir.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@
#ifndef COPYDIR_H
#define COPYDIR_H

/*
* Enumeration to denote copy modes.
*/
typedef enum CopyMethod
{
COPY_METHOD_COPY,
COPY_METHOD_COPY_FILE_RANGE,
} CopyMethod;

extern void copydir(const char *fromdir, const char *todir, bool recurse);
extern void copydir_with_method(const char *fromdir, const char *todir, bool recurse, CopyMethod copy_method);
extern void copy_file(const char *fromfile, const char *tofile);
extern void copy_file_by_range(const char *fromfile, const char *tofile);

#endif /* COPYDIR_H */