Coverage Report

Created: 2025-07-03 06:49

/src/postgres/src/backend/backup/basebackup_target.c
Line
Count
Source (jump to first uncovered line)
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-2025, PostgreSQL Global Development Group
10
 *
11
 * IDENTIFICATION
12
 *    src/backend/backup/basebackup_target.c
13
 *
14
 *-------------------------------------------------------------------------
15
 */
16
#include "postgres.h"
17
18
#include "backup/basebackup_target.h"
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 *);
26
} BaseBackupTargetType;
27
28
struct BaseBackupTargetHandle
29
{
30
  BaseBackupTargetType *type;
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
40
static BaseBackupTargetType builtin_backup_targets[] =
41
{
42
  {
43
    "blackhole", reject_target_detail, blackhole_get_sink
44
  },
45
  {
46
    "server", server_check_detail, server_get_sink
47
  },
48
  {
49
    NULL
50
  }
51
};
52
53
static List *BaseBackupTargetTypeList = NIL;
54
55
/*
56
 * Add a new base backup target type.
57
 *
58
 * This is intended for use by server extensions.
59
 */
60
void
61
BaseBackupAddTarget(char *name,
62
          void *(*check_detail) (char *, char *),
63
          bbsink *(*get_sink) (bbsink *, void *))
64
0
{
65
0
  BaseBackupTargetType *newtype;
66
0
  MemoryContext oldcontext;
67
0
  ListCell   *lc;
68
69
  /* If the target list is not yet initialized, do that first. */
70
0
  if (BaseBackupTargetTypeList == NIL)
71
0
    initialize_target_list();
72
73
  /* Search the target type list for an existing entry with this name. */
74
0
  foreach(lc, BaseBackupTargetTypeList)
75
0
  {
76
0
    BaseBackupTargetType *ttype = lfirst(lc);
77
78
0
    if (strcmp(ttype->name, name) == 0)
79
0
    {
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
0
      ttype->check_detail = check_detail;
88
0
      ttype->get_sink = get_sink;
89
0
      return;
90
0
    }
91
0
  }
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
   */
98
0
  oldcontext = MemoryContextSwitchTo(TopMemoryContext);
99
0
  newtype = palloc(sizeof(BaseBackupTargetType));
100
0
  newtype->name = pstrdup(name);
101
0
  newtype->check_detail = check_detail;
102
0
  newtype->get_sink = get_sink;
103
0
  BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, newtype);
104
0
  MemoryContextSwitchTo(oldcontext);
105
0
}
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
 */
116
BaseBackupTargetHandle *
117
BaseBackupGetTargetHandle(char *target, char *target_detail)
118
0
{
119
0
  ListCell   *lc;
120
121
  /* If the target list is not yet initialized, do that first. */
122
0
  if (BaseBackupTargetTypeList == NIL)
123
0
    initialize_target_list();
124
125
  /* Search the target type list for a match. */
126
0
  foreach(lc, BaseBackupTargetTypeList)
127
0
  {
128
0
    BaseBackupTargetType *ttype = lfirst(lc);
129
130
0
    if (strcmp(ttype->name, target) == 0)
131
0
    {
132
0
      BaseBackupTargetHandle *handle;
133
134
      /* Found the target. */
135
0
      handle = palloc(sizeof(BaseBackupTargetHandle));
136
0
      handle->type = ttype;
137
0
      handle->detail_arg = ttype->check_detail(target, target_detail);
138
139
0
      return handle;
140
0
    }
141
0
  }
142
143
  /* Did not find the target. */
144
0
  ereport(ERROR,
145
0
      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
146
0
       errmsg("unrecognized target: \"%s\"", target)));
147
148
  /* keep compiler quiet */
149
0
  return NULL;
150
0
}
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 *
163
BaseBackupGetSink(BaseBackupTargetHandle *handle, bbsink *next_sink)
164
0
{
165
0
  return handle->type->get_sink(next_sink, handle->detail_arg);
166
0
}
167
168
/*
169
 * Load predefined target types into BaseBackupTargetTypeList.
170
 */
171
static void
172
initialize_target_list(void)
173
0
{
174
0
  BaseBackupTargetType *ttype = builtin_backup_targets;
175
0
  MemoryContext oldcontext;
176
177
0
  oldcontext = MemoryContextSwitchTo(TopMemoryContext);
178
0
  while (ttype->name != NULL)
179
0
  {
180
0
    BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, ttype);
181
0
    ++ttype;
182
0
  }
183
0
  MemoryContextSwitchTo(oldcontext);
184
0
}
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
0
{
196
0
  return next_sink;
197
0
}
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
0
{
205
0
  return bbsink_server_new(next_sink, detail_arg);
206
0
}
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
0
{
215
0
  if (target_detail != NULL)
216
0
    ereport(ERROR,
217
0
        (errcode(ERRCODE_SYNTAX_ERROR),
218
0
         errmsg("target \"%s\" does not accept a target detail",
219
0
            target)));
220
221
0
  return NULL;
222
0
}
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
0
{
234
0
  if (target_detail == NULL)
235
0
    ereport(ERROR,
236
0
        (errcode(ERRCODE_SYNTAX_ERROR),
237
0
         errmsg("target \"%s\" requires a target detail",
238
0
            target)));
239
240
0
  return target_detail;
241
0
}