From 653b2a7d84052d5c60e6bf8527c9c251c873413c Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Fri, 1 May 2020 16:43:16 -0400
Subject: [PATCH v1 09/11] Introduce bbarchiver abstraction.

---
 src/backend/replication/Makefile              |   1 +
 src/backend/replication/basebackup_archiver.c | 119 +++++++++++
 src/include/replication/basebackup_archiver.h | 195 ++++++++++++++++++
 3 files changed, 315 insertions(+)
 create mode 100644 src/backend/replication/basebackup_archiver.c
 create mode 100644 src/include/replication/basebackup_archiver.h

diff --git a/src/backend/replication/Makefile b/src/backend/replication/Makefile
index 7de4f82882..aacccd350d 100644
--- a/src/backend/replication/Makefile
+++ b/src/backend/replication/Makefile
@@ -17,6 +17,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 OBJS = \
 	backup_manifest.o \
 	basebackup.o \
+	basebackup_archiver.o \
 	basebackup_libpq.o \
 	basebackup_progress.o \
 	basebackup_sink.o \
diff --git a/src/backend/replication/basebackup_archiver.c b/src/backend/replication/basebackup_archiver.c
new file mode 100644
index 0000000000..045a8a088e
--- /dev/null
+++ b/src/backend/replication/basebackup_archiver.c
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+ *
+ * basebackup_archiver.c
+ *	  general supporting code for basebackup archiver implementations
+ *
+ * Portions Copyright (c) 2010-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/replication/basebackup_archiver.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "replication/basebackup_archiver.h"
+
+/* Pass begin_tablespace callback to next bbarchiver. */
+void
+bbarchiver_forward_begin_tablespace(bbarchiver *archiver, tablespaceinfo *tsinfo)
+{
+	Assert(archiver->bba_next != NULL);
+	bbarchiver_begin_tablespace(archiver->bba_next, tsinfo);
+}
+
+/* Pass end_tablespace callback to next bbarchiver. */
+void
+bbarchiver_forward_end_tablespace(bbarchiver *archiver)
+{
+	Assert(archiver->bba_next != NULL);
+	bbarchiver_end_tablespace(archiver->bba_next);
+}
+
+/* Pass begin_file callback to next bbarchiver. */
+void
+bbarchiver_forward_begin_file(bbarchiver *archiver, const char *relative_path,
+							  struct stat *statbuf)
+{
+	Assert(archiver->bba_next != NULL);
+	bbarchiver_begin_file(archiver->bba_next, relative_path, statbuf);
+}
+
+/* Pass file_contents callback to next bbarchiver. */
+void
+bbarchiver_forward_file_contents(bbarchiver *archiver, const char *data, size_t len)
+{
+	Assert(archiver->bba_next != NULL);
+	bbarchiver_file_contents(archiver->bba_next, data, len);
+}
+
+/* Pass end_file callback to next bbarchiver. */
+void
+bbarchiver_forward_end_file(bbarchiver *archiver)
+{
+	Assert(archiver->bba_next != NULL);
+	bbarchiver_end_file(archiver->bba_next);
+}
+
+/* Pass directory callback to next bbarchiver. */
+void
+bbarchiver_forward_directory(bbarchiver *archiver, const char *relative_path,
+							 struct stat *statbuf)
+{
+	Assert(archiver->bba_next != NULL);
+	bbarchiver_directory(archiver->bba_next, relative_path, statbuf);
+}
+
+/* Pass symbolic_link callback to next bbarchiver. */
+void
+bbarchiver_forward_symbolic_link(bbarchiver *archiver, const char *relative_path,
+								 const char *linktarget, struct stat *statbuf)
+{
+	Assert(archiver->bba_next != NULL);
+	bbarchiver_symbolic_link(archiver->bba_next, relative_path, linktarget, statbuf);
+}
+
+/* Ignore begin_tablespace callback. */
+void
+bbarchiver_noop_begin_tablespace(bbarchiver *archiver, tablespaceinfo *tsinfo)
+{
+	/* Do nothing */
+}
+
+/* Ignore end_tablespace callback. */
+void
+bbarchiver_noop_end_tablespace(bbarchiver *archiver)
+{
+	/* Do nothing */
+}
+
+/* Ignore begin_file callback. */
+void
+bbarchiver_noop_begin_file(bbarchiver *archiver, const char *relative_path,
+							  struct stat *statbuf)
+{
+	/* Do nothing */
+}
+
+/* Ignore end_file callback. */
+void
+bbarchiver_noop_end_file(bbarchiver *archiver)
+{
+	/* Do nothing */
+}
+
+/* Ignore directory callback. */
+void
+bbarchiver_noop_directory(bbarchiver *archiver, const char *relative_path,
+							 struct stat *statbuf)
+{
+	/* Do nothing */
+}
+
+/* Ignore symbolic_link callback. */
+void
+bbarchiver_noop_symbolic_link(bbarchiver *archiver, const char *relative_path,
+								 const char *linktarget, struct stat *statbuf)
+{
+	/* Do nothing */
+}
diff --git a/src/include/replication/basebackup_archiver.h b/src/include/replication/basebackup_archiver.h
new file mode 100644
index 0000000000..fce0afa167
--- /dev/null
+++ b/src/include/replication/basebackup_archiver.h
@@ -0,0 +1,195 @@
+/*-------------------------------------------------------------------------
+ *
+ * bbarchiver.h
+ *	  iterate over files, directories, and symbolic links encountered as
+ *	  part of the base backup process
+ *
+ * Portions Copyright (c) 2010-2020, PostgreSQL Global Development Group
+ *
+ * src/include/replication/bbarchiver.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef BASEBACKUP_ARCHIVER_H
+#define BASEBACKUP_ARCHIVER_H
+
+#include <sys/stat.h>
+
+#include "replication/basebackup.h"
+#include "replication/basebackup_sink.h"
+
+struct bbarchiver;
+struct bbarchiver_ops;
+typedef struct bbarchiver bbarchiver;
+typedef struct bbarchiver_ops bbarchiver_ops;
+
+/*
+ * Common data for any type of basebackup archiver.
+ *
+ * 'bba_ops' is the relevant callback table.
+ *
+ * 'bba_next' is a pointer to another bbarchiver to which this bbarchiver is
+ * forwarding some or all operations.
+ *
+ * If a barchiver needs to store additional state, it can allocate a larger
+ * structure whose first element is a bbarchiver.
+ */
+struct bbarchiver
+{
+	const bbarchiver_ops *bba_ops;
+	bbarchiver *bba_next;
+};
+
+/*
+ * Callbacks for a backup archiver.
+ *
+ * Except as otherwise noted, all of these callbacks are required. If a particular
+ * callback just needs to forward the call to archiver->bba_next, use
+ * bbarchiver_forward_<callback_name> as the callback. If a particular (required)
+ * callback doesn't need to do anything at all, use bbarchiver_noop_<callback_name>
+ * as the callback.
+ *
+ * Callers should always invoke these callbacks via the bbarchiver_*
+ * inline functions rather than calling them directly.
+ */
+struct bbarchiver_ops
+{
+	/* These callbacks are invoked just before and after visiting each tablespace. */
+	void (*begin_tablespace)(bbarchiver *archiver, tablespaceinfo *tsinfo);
+	void (*end_tablespace)(bbarchiver *archiver);
+
+	/* This callback is invoked each time we begin visiting a plain file. */
+	void (*begin_file)(bbarchiver *archiver, const char *relative_path,
+					   struct stat *statbuf);
+
+	/*
+	 * This callback is invoked one or more times for each plain file, with the
+	 * contents of the file passed to it chunk by chunk.
+	 *
+	 * It is optional. If NULL, the file is not read.
+	 */
+	void (*file_contents)(bbarchiver *archiver, const char *data,
+						  size_t len);
+
+	/* This callback is invoked each time we finish visiting a plain file. */
+	void (*end_file)(bbarchiver *archiver);
+
+	/* This method gets called each time we visit a directory. */
+	void (*directory)(bbarchiver *archiver, const char *relative_path,
+					  struct stat *statbuf);
+
+	/* This method gets called each time we visit a symbolic link. */
+	void (*symbolic_link)(bbarchiver *archiver, const char *relative_path,
+						  const char *linktarget, struct stat *statbuf);
+};
+
+/* Dummy callbacks for when a bbarchiver wants to forward operations. */
+extern void bbarchiver_forward_begin_tablespace(bbarchiver *archiver,
+											   tablespaceinfo *tsinfo);
+extern void bbarchiver_forward_end_tablespace(bbarchiver *archiver);
+extern void bbarchiver_forward_begin_file(bbarchiver *archiver,
+										  const char *relative_path,
+										  struct stat *statbuf);
+extern void bbarchiver_forward_file_contents(bbarchiver *archiver,
+											 const char *data, size_t len);
+extern void bbarchiver_forward_end_file(bbarchiver *archiver);
+extern void bbarchiver_forward_directory(bbarchiver *archiver,
+										 const char *relative_path,
+										 struct stat *statbuf);
+extern void bbarchiver_forward_symbolic_link(bbarchiver *archiver,
+											 const char *relative_path,
+											 const char *linktarget,
+											 struct stat *statbuf);
+
+/* Dummy callbacks for when a bbarchiver wants to do nothing. */
+extern void bbarchiver_noop_begin_tablespace(bbarchiver *archiver,
+											 tablespaceinfo *tsinfo);
+extern void bbarchiver_noop_end_tablespace(bbarchiver *archiver);
+extern void bbarchiver_noop_begin_file(bbarchiver *archiver,
+									   const char *relative_path,
+									   struct stat *statbuf);
+/* if there's nothing to do for file contents, omit callback! */
+extern void bbarchiver_noop_end_file(bbarchiver *archiver);
+extern void bbarchiver_noop_directory(bbarchiver *archiver,
+									  const char *relative_path,
+									  struct stat *statbuf);
+extern void bbarchiver_noop_symbolic_link(bbarchiver *archiver,
+										  const char *relative_path,
+										  const char *linktarget,
+										  struct stat *statbuf);
+
+/* Begin visiting a tablespace. */
+static inline void
+bbarchiver_begin_tablespace(bbarchiver *archiver, tablespaceinfo *tsinfo)
+{
+	Assert(archiver->bba_ops->begin_tablespace != NULL);
+	archiver->bba_ops->begin_tablespace(archiver, tsinfo);
+}
+
+/* Finish visiting a tablespace. */
+static inline void
+bbarchiver_end_tablespace(bbarchiver *archiver)
+{
+	Assert(archiver->bba_ops->end_tablespace != NULL);
+	archiver->bba_ops->end_tablespace(archiver);
+}
+
+/* Begin visiting a plain file. */
+static inline void
+bbarchiver_begin_file(bbarchiver *archiver, const char *relative_path,
+					  struct stat *statbuf)
+{
+	Assert(archiver->bba_ops->begin_file != NULL);
+	archiver->bba_ops->begin_file(archiver, relative_path, statbuf);
+}
+
+/* Does this archiver need the contents of the files? */
+static inline bool
+bbarchiver_needs_file_contents(bbarchiver *archiver)
+{
+	return archiver->bba_ops->file_contents != NULL;
+}
+
+/*
+ * Process contents of a plain file.
+ *
+ * Don't call this unless bbarchiver_needs_file_contents returns true.
+ */
+static inline void
+bbarchiver_file_contents(bbarchiver *archiver, const char *data, size_t len)
+{
+	Assert(archiver->bba_ops->file_contents != NULL);
+	archiver->bba_ops->file_contents(archiver, data, len);
+}
+
+/* Finish visiting a plain file. */
+static inline void
+bbarchiver_end_file(bbarchiver *archiver)
+{
+	Assert(archiver->bba_ops->end_file != NULL);
+	archiver->bba_ops->end_file(archiver);
+}
+
+/* Visit a directory. */
+static inline void
+bbarchiver_directory(bbarchiver *archiver, const char *relative_path,
+					 struct stat *statbuf)
+{
+	Assert(archiver->bba_ops->directory != NULL);
+	archiver->bba_ops->directory(archiver, relative_path, statbuf);
+}
+
+/* Visit a symbolic link. */
+static inline void
+bbarchiver_symbolic_link(bbarchiver *archiver, const char *relative_path,
+						 const char *linktarget, struct stat *statbuf)
+{
+	Assert(archiver->bba_ops->symbolic_link != NULL);
+	archiver->bba_ops->symbolic_link(archiver, relative_path, linktarget, statbuf);
+}
+
+/* Constructors for various types of archivers. */
+extern bbarchiver *bbarchiver_tar_new(bbsink *sink);
+extern bbarchiver *bbarchiver_tarsize_new(void);
+
+#endif
-- 
2.24.2 (Apple Git-127)

