summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/storage/file/copydir.c80
-rw-r--r--src/backend/utils/activity/wait_event_names.txt1
-rw-r--r--src/backend/utils/misc/guc_tables.c22
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample4
-rw-r--r--src/include/storage/copydir.h9
-rw-r--r--src/tools/pgindent/typedefs.list1
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