diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/storage/file/copydir.c | 80 | ||||
-rw-r--r-- | src/backend/utils/activity/wait_event_names.txt | 1 | ||||
-rw-r--r-- | src/backend/utils/misc/guc_tables.c | 22 | ||||
-rw-r--r-- | src/backend/utils/misc/postgresql.conf.sample | 4 | ||||
-rw-r--r-- | src/include/storage/copydir.h | 9 | ||||
-rw-r--r-- | src/tools/pgindent/typedefs.list | 1 |
6 files changed, 116 insertions, 1 deletions
diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c index c335b60a367..aa8c64a2c9e 100644 --- a/src/backend/storage/file/copydir.c +++ b/src/backend/storage/file/copydir.c @@ -18,6 +18,9 @@ #include "postgres.h" +#ifdef HAVE_COPYFILE_H +#include <copyfile.h> +#endif #include <fcntl.h> #include <unistd.h> @@ -27,11 +30,19 @@ #include "storage/copydir.h" #include "storage/fd.h" +/* GUCs */ +int file_copy_method = FILE_COPY_METHOD_COPY; + +static void clone_file(const char *fromfile, const char *tofile); + /* * copydir: copy a directory * * If recurse is false, subdirectories are ignored. Anything that's not * a directory or a regular file is ignored. + * + * This function uses the file_copy_method GUC. New uses of this function must + * be documented in doc/src/sgml/config.sgml. */ void copydir(const char *fromdir, const char *todir, bool recurse) @@ -71,7 +82,12 @@ copydir(const char *fromdir, const char *todir, bool recurse) copydir(fromfile, tofile, true); } else if (xlde_type == PGFILETYPE_REG) - copy_file(fromfile, tofile); + { + if (file_copy_method == FILE_COPY_METHOD_CLONE) + clone_file(fromfile, tofile); + else + copy_file(fromfile, tofile); + } } FreeDir(xldir); @@ -214,3 +230,65 @@ copy_file(const char *fromfile, const char *tofile) pfree(buffer); } + +/* + * clone one file + */ +static void +clone_file(const char *fromfile, const char *tofile) +{ +#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE) + if (copyfile(fromfile, tofile, NULL, COPYFILE_CLONE_FORCE) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not clone file \"%s\" to \"%s\": %m", + fromfile, tofile))); +#elif defined(HAVE_COPY_FILE_RANGE) + int srcfd; + int dstfd; + ssize_t nbytes; + + 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_WRONLY | O_CREAT | O_EXCL | PG_BINARY); + if (dstfd < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not create file \"%s\": %m", tofile))); + + do + { + /* + * Don't copy too much at once, so we can check for interrupts from + * time to time if it falls back to a slow copy. + */ + CHECK_FOR_INTERRUPTS(); + pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_COPY); + nbytes = copy_file_range(srcfd, NULL, dstfd, NULL, 1024 * 1024, 0); + if (nbytes < 0 && errno != EINTR) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not clone file \"%s\" to \"%s\": %m", + fromfile, tofile))); + pgstat_report_wait_end(); + } + 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 + /* If there is no CLONE support this function should not be called. */ + pg_unreachable(); +#endif +} diff --git a/src/backend/utils/activity/wait_event_names.txt b/src/backend/utils/activity/wait_event_names.txt index 23eaf559c8d..930321905f1 100644 --- a/src/backend/utils/activity/wait_event_names.txt +++ b/src/backend/utils/activity/wait_event_names.txt @@ -208,6 +208,7 @@ CONTROL_FILE_SYNC "Waiting for the <filename>pg_control</filename> file to reach CONTROL_FILE_SYNC_UPDATE "Waiting for an update to the <filename>pg_control</filename> file to reach durable storage." CONTROL_FILE_WRITE "Waiting for a write to the <filename>pg_control</filename> file." CONTROL_FILE_WRITE_UPDATE "Waiting for a write to update the <filename>pg_control</filename> file." +COPY_FILE_COPY "Waiting for a file copy operation." COPY_FILE_READ "Waiting for a read during a file copy operation." COPY_FILE_WRITE "Waiting for a write during a file copy operation." DATA_FILE_EXTEND "Waiting for a relation data file to be extended." diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index d54df555fba..60b12446a1c 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -20,6 +20,9 @@ */ #include "postgres.h" +#ifdef HAVE_COPYFILE_H +#include <copyfile.h> +#endif #include <float.h> #include <limits.h> #ifdef HAVE_SYSLOG @@ -76,6 +79,7 @@ #include "storage/aio.h" #include "storage/bufmgr.h" #include "storage/bufpage.h" +#include "storage/copydir.h" #include "storage/io_worker.h" #include "storage/large_object.h" #include "storage/pg_shmem.h" @@ -479,6 +483,14 @@ static const struct config_enum_entry wal_compression_options[] = { {NULL, 0, false} }; +static const struct config_enum_entry file_copy_method_options[] = { + {"copy", FILE_COPY_METHOD_COPY, false}, +#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE) || defined(HAVE_COPY_FILE_RANGE) + {"clone", FILE_COPY_METHOD_CLONE, false}, +#endif + {NULL, 0, false} +}; + /* * Options for enum values stored in other modules */ @@ -5243,6 +5255,16 @@ struct config_enum ConfigureNamesEnum[] = }, { + {"file_copy_method", PGC_USERSET, RESOURCES_DISK, + gettext_noop("Selects the file copy method."), + NULL + }, + &file_copy_method, + FILE_COPY_METHOD_COPY, file_copy_method_options, + NULL, NULL, NULL + }, + + { {"wal_sync_method", PGC_SIGHUP, WAL_SETTINGS, gettext_noop("Selects the method used for forcing WAL updates to disk."), NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 25fe90a430f..34826d01380 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -183,6 +183,10 @@ #max_notify_queue_pages = 1048576 # limits the number of SLRU pages allocated # for NOTIFY / LISTEN queue +#file_copy_method = copy # the default is the first option + # copy + # clone (if system support is available) + # - Kernel Resources - #max_files_per_process = 1000 # min 64 diff --git a/src/include/storage/copydir.h b/src/include/storage/copydir.h index cf60e63f4e2..940d74462d1 100644 --- a/src/include/storage/copydir.h +++ b/src/include/storage/copydir.h @@ -13,6 +13,15 @@ #ifndef COPYDIR_H #define COPYDIR_H +typedef enum FileCopyMethod +{ + FILE_COPY_METHOD_COPY, + FILE_COPY_METHOD_CLONE, +} FileCopyMethod; + +/* GUC parameters */ +extern PGDLLIMPORT int file_copy_method; + extern void copydir(const char *fromdir, const char *todir, bool recurse); extern void copy_file(const char *fromfile, const char *tofile); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 780e4c4fc07..d16bc208654 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -804,6 +804,7 @@ FieldSelect FieldStore File FileBackupMethod +FileCopyMethod FileFdwExecutionState FileFdwPlanState FileNameMap |