LCOV - code coverage report
Current view: top level - src/backend/backup - basebackup_target.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.8 % 49 44
Test Date: 2026-03-03 02:14:47 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       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-2026, 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            1 : BaseBackupAddTarget(char *name,
      62              :                     void *(*check_detail) (char *, char *),
      63              :                     bbsink *(*get_sink) (bbsink *, void *))
      64              : {
      65              :     BaseBackupTargetType *newtype;
      66              :     MemoryContext oldcontext;
      67              :     ListCell   *lc;
      68              : 
      69              :     /* If the target list is not yet initialized, do that first. */
      70            1 :     if (BaseBackupTargetTypeList == NIL)
      71            1 :         initialize_target_list();
      72              : 
      73              :     /* Search the target type list for an existing entry with this name. */
      74            3 :     foreach(lc, BaseBackupTargetTypeList)
      75              :     {
      76            2 :         BaseBackupTargetType *ttype = lfirst(lc);
      77              : 
      78            2 :         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            0 :             ttype->check_detail = check_detail;
      88            0 :             ttype->get_sink = get_sink;
      89            0 :             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              :      */
      98            1 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
      99            1 :     newtype = palloc_object(BaseBackupTargetType);
     100            1 :     newtype->name = pstrdup(name);
     101            1 :     newtype->check_detail = check_detail;
     102            1 :     newtype->get_sink = get_sink;
     103            1 :     BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, newtype);
     104            1 :     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              :  */
     116              : BaseBackupTargetHandle *
     117           14 : BaseBackupGetTargetHandle(char *target, char *target_detail)
     118              : {
     119              :     ListCell   *lc;
     120              : 
     121              :     /* If the target list is not yet initialized, do that first. */
     122           14 :     if (BaseBackupTargetTypeList == NIL)
     123            8 :         initialize_target_list();
     124              : 
     125              :     /* Search the target type list for a match. */
     126           34 :     foreach(lc, BaseBackupTargetTypeList)
     127              :     {
     128           33 :         BaseBackupTargetType *ttype = lfirst(lc);
     129              : 
     130           33 :         if (strcmp(ttype->name, target) == 0)
     131              :         {
     132              :             BaseBackupTargetHandle *handle;
     133              : 
     134              :             /* Found the target. */
     135           13 :             handle = palloc_object(BaseBackupTargetHandle);
     136           13 :             handle->type = ttype;
     137           13 :             handle->detail_arg = ttype->check_detail(target, target_detail);
     138              : 
     139           12 :             return handle;
     140              :         }
     141              :     }
     142              : 
     143              :     /* Did not find the target. */
     144            1 :     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 *
     163           12 : BaseBackupGetSink(BaseBackupTargetHandle *handle, bbsink *next_sink)
     164              : {
     165           12 :     return handle->type->get_sink(next_sink, handle->detail_arg);
     166              : }
     167              : 
     168              : /*
     169              :  * Load predefined target types into BaseBackupTargetTypeList.
     170              :  */
     171              : static void
     172            9 : initialize_target_list(void)
     173              : {
     174            9 :     BaseBackupTargetType *ttype = builtin_backup_targets;
     175              :     MemoryContext oldcontext;
     176              : 
     177            9 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     178           27 :     while (ttype->name != NULL)
     179              :     {
     180           18 :         BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, ttype);
     181           18 :         ++ttype;
     182              :     }
     183            9 :     MemoryContextSwitchTo(oldcontext);
     184            9 : }
     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            1 : blackhole_get_sink(bbsink *next_sink, void *detail_arg)
     195              : {
     196            1 :     return next_sink;
     197              : }
     198              : 
     199              : /*
     200              :  * Create a bbsink implementing a server-side backup.
     201              :  */
     202              : static bbsink *
     203            6 : server_get_sink(bbsink *next_sink, void *detail_arg)
     204              : {
     205            6 :     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            1 : reject_target_detail(char *target, char *target_detail)
     214              : {
     215            1 :     if (target_detail != NULL)
     216            0 :         ereport(ERROR,
     217              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     218              :                  errmsg("target \"%s\" does not accept a target detail",
     219              :                         target)));
     220              : 
     221            1 :     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            6 : server_check_detail(char *target, char *target_detail)
     233              : {
     234            6 :     if (target_detail == NULL)
     235            0 :         ereport(ERROR,
     236              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     237              :                  errmsg("target \"%s\" requires a target detail",
     238              :                         target)));
     239              : 
     240            6 :     return target_detail;
     241              : }
        

Generated by: LCOV version 2.0-1