LCOV - code coverage report
Current view: top level - src/backend/backup - basebackup_target.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 44 49 89.8 %
Date: 2024-02-28 23:11:08 Functions: 8 8 100.0 %
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-2024, 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           2 : 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           2 :     if (BaseBackupTargetTypeList == NIL)
      71           2 :         initialize_target_list();
      72             : 
      73             :     /* Search the target type list for an existing entry with this name. */
      74           6 :     foreach(lc, BaseBackupTargetTypeList)
      75             :     {
      76           4 :         BaseBackupTargetType *ttype = lfirst(lc);
      77             : 
      78           4 :         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           2 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
      99           2 :     newtype = palloc(sizeof(BaseBackupTargetType));
     100           2 :     newtype->name = pstrdup(name);
     101           2 :     newtype->check_detail = check_detail;
     102           2 :     newtype->get_sink = get_sink;
     103           2 :     BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, newtype);
     104           2 :     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          24 : BaseBackupGetTargetHandle(char *target, char *target_detail)
     118             : {
     119             :     ListCell   *lc;
     120             : 
     121             :     /* If the target list is not yet initialized, do that first. */
     122          24 :     if (BaseBackupTargetTypeList == NIL)
     123          12 :         initialize_target_list();
     124             : 
     125             :     /* Search the target type list for a match. */
     126          60 :     foreach(lc, BaseBackupTargetTypeList)
     127             :     {
     128          58 :         BaseBackupTargetType *ttype = lfirst(lc);
     129             : 
     130          58 :         if (strcmp(ttype->name, target) == 0)
     131             :         {
     132             :             BaseBackupTargetHandle *handle;
     133             : 
     134             :             /* Found the target. */
     135          22 :             handle = palloc(sizeof(BaseBackupTargetHandle));
     136          22 :             handle->type = ttype;
     137          22 :             handle->detail_arg = ttype->check_detail(target, target_detail);
     138             : 
     139          20 :             return handle;
     140             :         }
     141             :     }
     142             : 
     143             :     /* Did not find the target. */
     144           2 :     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          20 : BaseBackupGetSink(BaseBackupTargetHandle *handle, bbsink *next_sink)
     164             : {
     165          20 :     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          14 : initialize_target_list(void)
     173             : {
     174          14 :     BaseBackupTargetType *ttype = builtin_backup_targets;
     175             :     MemoryContext oldcontext;
     176             : 
     177          14 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     178          42 :     while (ttype->name != NULL)
     179             :     {
     180          28 :         BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, ttype);
     181          28 :         ++ttype;
     182             :     }
     183          14 :     MemoryContextSwitchTo(oldcontext);
     184          14 : }
     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           2 : blackhole_get_sink(bbsink *next_sink, void *detail_arg)
     195             : {
     196           2 :     return next_sink;
     197             : }
     198             : 
     199             : /*
     200             :  * Create a bbsink implementing a server-side backup.
     201             :  */
     202             : static bbsink *
     203           8 : server_get_sink(bbsink *next_sink, void *detail_arg)
     204             : {
     205           8 :     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           2 : reject_target_detail(char *target, char *target_detail)
     214             : {
     215           2 :     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           2 :     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           8 : server_check_detail(char *target, char *target_detail)
     233             : {
     234           8 :     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           8 :     return target_detail;
     241             : }

Generated by: LCOV version 1.14