PostgreSQL Source Code  git master
basebackup_target.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * basebackup_target.c
4  * Base backups can be "targeted", which means that they can be sent
5  * somewhere other than to the client which requested the backup.
6  * Furthermore, new targets can be defined by extensions. This file
7  * contains code to support that functionality.
8  *
9  * Portions Copyright (c) 2010-2022, PostgreSQL Global Development Group
10  *
11  * IDENTIFICATION
12  * src/backend/replication/basebackup_target.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17 
19 #include "utils/memutils.h"
20 
21 typedef struct BaseBackupTargetType
22 {
23  char *name;
24  void *(*check_detail) (char *, char *);
25  bbsink *(*get_sink) (bbsink *, void *);
27 
29 {
31  void *detail_arg;
32 };
33 
34 static void initialize_target_list(void);
35 static bbsink *blackhole_get_sink(bbsink *next_sink, void *detail_arg);
36 static bbsink *server_get_sink(bbsink *next_sink, void *detail_arg);
37 static void *reject_target_detail(char *target, char *target_detail);
38 static void *server_check_detail(char *target, char *target_detail);
39 
41 {
42  {
44  },
45  {
47  },
48  {
49  NULL
50  }
51 };
52 
54 
55 /*
56  * Add a new base backup target type.
57  *
58  * This is intended for use by server extensions.
59  */
60 void
62  void *(*check_detail) (char *, char *),
63  bbsink *(*get_sink) (bbsink *, void *))
64 {
65  BaseBackupTargetType *ttype;
66  MemoryContext oldcontext;
67  ListCell *lc;
68 
69  /* If the target list is not yet initialized, do that first. */
72 
73  /* Search the target type list for an existing entry with this name. */
74  foreach(lc, BaseBackupTargetTypeList)
75  {
76  BaseBackupTargetType *ttype = lfirst(lc);
77 
78  if (strcmp(ttype->name, name) == 0)
79  {
80  /*
81  * We found one, so update it.
82  *
83  * It is probably not a great idea to call BaseBackupAddTarget for
84  * the same name multiple times, but if it happens, this seems
85  * like the sanest behavior.
86  */
87  ttype->check_detail = check_detail;
88  ttype->get_sink = get_sink;
89  return;
90  }
91  }
92 
93  /*
94  * We use TopMemoryContext for allocations here to make sure that the data
95  * we need doesn't vanish under us; that's also why we copy the target
96  * name into a newly-allocated chunk of memory.
97  */
99  ttype = palloc(sizeof(BaseBackupTargetType));
100  ttype->name = pstrdup(name);
101  ttype->check_detail = check_detail;
102  ttype->get_sink = get_sink;
104  MemoryContextSwitchTo(oldcontext);
105 }
106 
107 /*
108  * Look up a base backup target and validate the target_detail.
109  *
110  * Extensions that define new backup targets will probably define a new
111  * type of bbsink to match. Validation of the target_detail can be performed
112  * either in the check_detail routine called here, or in the bbsink
113  * constructor, which will be called from BaseBackupGetSink. It's mostly
114  * a matter of taste, but the check_detail function runs somewhat earlier.
115  */
117 BaseBackupGetTargetHandle(char *target, char *target_detail)
118 {
119  ListCell *lc;
120 
121  /* If the target list is not yet initialized, do that first. */
124 
125  /* Search the target type list for a match. */
126  foreach(lc, BaseBackupTargetTypeList)
127  {
128  BaseBackupTargetType *ttype = lfirst(lc);
129 
130  if (strcmp(ttype->name, target) == 0)
131  {
132  BaseBackupTargetHandle *handle;
133 
134  /* Found the target. */
135  handle = palloc(sizeof(BaseBackupTargetHandle));
136  handle->type = ttype;
137  handle->detail_arg = ttype->check_detail(target, target_detail);
138 
139  return handle;
140  }
141  }
142 
143  /* Did not find the target. */
144  ereport(ERROR,
145  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
146  errmsg("unrecognized target: \"%s\"", target)));
147 
148  /* keep compiler quiet */
149  return NULL;
150 }
151 
152 /*
153  * Construct a bbsink that will implement the backup target.
154  *
155  * The get_sink function does all the real work, so all we have to do here
156  * is call it with the correct arguments. Whatever the check_detail function
157  * returned is here passed through to the get_sink function. This lets those
158  * two functions communicate with each other, if they wish. If not, the
159  * check_detail function can simply return the target_detail and let the
160  * get_sink function take it from there.
161  */
162 bbsink *
164 {
165  return handle->type->get_sink(next_sink, handle->detail_arg);
166 }
167 
168 /*
169  * Load predefined target types into BaseBackupTargetTypeList.
170  */
171 static void
173 {
175  MemoryContext oldcontext;
176 
178  while (ttype->name != NULL)
179  {
181  ++ttype;
182  }
183  MemoryContextSwitchTo(oldcontext);
184 }
185 
186 /*
187  * Normally, a get_sink function should construct and return a new bbsink that
188  * implements the backup target, but the 'blackhole' target just throws the
189  * data away. We could implement that by adding a bbsink that does nothing
190  * but forward, but it's even cheaper to implement that by not adding a bbsink
191  * at all.
192  */
193 static bbsink *
194 blackhole_get_sink(bbsink *next_sink, void *detail_arg)
195 {
196  return next_sink;
197 }
198 
199 /*
200  * Create a bbsink implementing a server-side backup.
201  */
202 static bbsink *
203 server_get_sink(bbsink *next_sink, void *detail_arg)
204 {
205  return bbsink_server_new(next_sink, detail_arg);
206 }
207 
208 /*
209  * Implement target-detail checking for a target that does not accept a
210  * detail.
211  */
212 static void *
213 reject_target_detail(char *target, char *target_detail)
214 {
215  if (target_detail != NULL)
216  ereport(ERROR,
217  (errcode(ERRCODE_SYNTAX_ERROR),
218  errmsg("target '%s' does not accept a target detail",
219  target)));
220 
221  return NULL;
222 }
223 
224 /*
225  * Implement target-detail checking for a server-side backup.
226  *
227  * target_detail should be the name of the directory to which the backup
228  * should be written, but we don't check that here. Rather, that check,
229  * as well as the necessary permissions checking, happens in bbsink_server_new.
230  */
231 static void *
232 server_check_detail(char *target, char *target_detail)
233 {
234  if (target_detail == NULL)
235  ereport(ERROR,
236  (errcode(ERRCODE_SYNTAX_ERROR),
237  errmsg("target '%s' requires a target detail",
238  target)));
239 
240  return target_detail;
241 }
bbsink * bbsink_server_new(bbsink *next, char *pathname)
struct BaseBackupTargetType BaseBackupTargetType
static void * reject_target_detail(char *target, char *target_detail)
bbsink * BaseBackupGetSink(BaseBackupTargetHandle *handle, bbsink *next_sink)
BaseBackupTargetHandle * BaseBackupGetTargetHandle(char *target, char *target_detail)
static bbsink * server_get_sink(bbsink *next_sink, void *detail_arg)
static List * BaseBackupTargetTypeList
static bbsink * blackhole_get_sink(bbsink *next_sink, void *detail_arg)
static BaseBackupTargetType builtin_backup_targets[]
static void * server_check_detail(char *target, char *target_detail)
void BaseBackupAddTarget(char *name, void *(*check_detail)(char *, char *), bbsink *(*get_sink)(bbsink *, void *))
static void initialize_target_list(void)
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define ereport(elevel,...)
Definition: elog.h:143
const char * name
Definition: encode.c:561
List * lappend(List *list, void *datum)
Definition: list.c:336
char * pstrdup(const char *in)
Definition: mcxt.c:1305
MemoryContext TopMemoryContext
Definition: mcxt.c:48
void * palloc(Size size)
Definition: mcxt.c:1068
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define lfirst(lc)
Definition: pg_list.h:169
#define NIL
Definition: pg_list.h:65
BaseBackupTargetType * type
void *(* check_detail)(char *, char *)
bbsink *(* get_sink)(bbsink *, void *)
Definition: pg_list.h:51