@@ -30,6 +30,8 @@ typedef struct TablespaceListCell
3030 struct TablespaceListCell * next ;
3131 char old_dir [MAXPGPATH ];
3232 char new_dir [MAXPGPATH ];
33+ bool checked ; /* If this mapping was checked during
34+ restore */
3335} TablespaceListCell ;
3436
3537typedef struct TablespaceList
@@ -54,6 +56,7 @@ typedef struct TablespaceCreatedList
5456static void restore_database (pgBackup * backup );
5557static void restore_directories (const char * pg_data_dir ,
5658 const char * backup_dir );
59+ static void check_tablespace_mapping (pgBackup * backup );
5760static void create_recovery_conf (time_t backup_id ,
5861 const char * target_time ,
5962 const char * target_xid ,
@@ -82,20 +85,25 @@ do_restore(time_t backup_id,
8285{
8386 int i ;
8487 int base_index ; /* index of base (full) backup */
88+ int last_diff_index = -1 ; /* index of last differential backup */
8589 int ret ;
8690 parray * backups ;
8791
8892 parray * timelines ;
8993 pgBackup * base_backup = NULL ;
9094 pgBackup * dest_backup = NULL ;
9195 pgRecoveryTarget * rt = NULL ;
92- bool backup_id_found = false;
96+ bool need_recovery_conf = false;
9397
9498 /* PGDATA and ARCLOG_PATH are always required */
9599 if (pgdata == NULL )
96100 elog (ERROR ,
97101 "required parameter not specified: PGDATA (-D, --pgdata)" );
98102
103+ /* Check if restore destination empty */
104+ if (!dir_is_empty (pgdata ))
105+ elog (ERROR , "restore destination is not empty: \"%s\"" , pgdata );
106+
99107 elog (LOG , "========================================" );
100108 elog (LOG , "restore start" );
101109
@@ -131,110 +139,91 @@ do_restore(time_t backup_id,
131139 elog (LOG , "searching recent full backup" );
132140 for (i = 0 ; i < parray_num (backups ); i ++ )
133141 {
142+ bool satisfied = false;
143+
134144 base_backup = (pgBackup * ) parray_get (backups , i );
135145
136146 if (backup_id && base_backup -> start_time > backup_id )
137147 continue ;
138148
139- if (backup_id == base_backup -> start_time &&
140- base_backup -> status == BACKUP_STATUS_OK )
149+ if (backup_id == base_backup -> start_time )
141150 {
142- backup_id_found = true;
151+ /* Checks for target backup */
152+ if (base_backup -> status != BACKUP_STATUS_OK )
153+ elog (ERROR , "given backup %s is in %s status" ,
154+ base36enc (backup_id ), status2str (base_backup -> status ));
155+
143156 dest_backup = base_backup ;
144157 }
145158
146- if (backup_id == base_backup -> start_time &&
147- base_backup -> status != BACKUP_STATUS_OK )
148- elog (ERROR , "given backup %s is %s" , base36enc (backup_id ),
149- status2str (base_backup -> status ));
150-
151159 if (dest_backup != NULL &&
152160 base_backup -> backup_mode == BACKUP_MODE_FULL &&
153161 base_backup -> status != BACKUP_STATUS_OK )
154- elog (ERROR , "base backup %s for given backup %s is %s " ,
162+ elog (ERROR , "base backup %s for given backup %s is in %s status " ,
155163 base36enc (base_backup -> start_time ),
156164 base36enc (dest_backup -> start_time ),
157165 status2str (base_backup -> status ));
158166
159- if (base_backup -> backup_mode < BACKUP_MODE_FULL ||
160- base_backup -> status != BACKUP_STATUS_OK )
167+ /* Dont check error backups */
168+ if (base_backup -> status != BACKUP_STATUS_OK ||
169+ /* Dont check differential backups if we found latest */
170+ (last_diff_index >= 0 && base_backup -> backup_mode != BACKUP_MODE_FULL ))
161171 continue ;
162172
163173 if (target_tli )
164174 {
165175 if (satisfy_timeline (timelines , base_backup ) &&
166176 satisfy_recovery_target (base_backup , rt ) &&
167- (backup_id_found || backup_id == 0 ))
168- goto base_backup_found ;
177+ (dest_backup || backup_id == 0 ))
178+ satisfied = true ;
169179 }
170180 else
171181 if (satisfy_recovery_target (base_backup , rt ) &&
172- (backup_id_found || backup_id == 0 ))
173- goto base_backup_found ;
182+ (dest_backup || backup_id == 0 ))
183+ satisfied = true ;
174184
175- backup_id_found = false;
185+ /* Target backup should satisfy restore options */
186+ if (backup_id == base_backup -> start_time && !satisfied )
187+ elog (ERROR , "backup %s does not satisfy restore options" ,
188+ base36enc (base_backup -> start_time ));
189+
190+ if (satisfied )
191+ {
192+ if (base_backup -> backup_mode != BACKUP_MODE_FULL )
193+ last_diff_index = i ;
194+ else
195+ goto base_backup_found ;
196+ }
176197 }
177198 /* no full backup found, cannot restore */
178- elog (ERROR , "no full backup found, cannot restore. " );
199+ elog (ERROR , "no full backup found, cannot restore" );
179200
180201base_backup_found :
181202 base_index = i ;
203+ if (last_diff_index == -1 )
204+ last_diff_index = base_index ;
182205
183- /* Check if restore destination empty */
184- if (!dir_is_empty (pgdata ))
185- elog (ERROR , "restore destination is not empty" );
186-
187- print_backup_lsn (base_backup );
188-
189- if (backup_id != 0 )
190- stream_wal = base_backup -> stream ;
191-
192- /* restore base backup */
193- restore_database (base_backup );
194-
195- /* restore following differential backup */
196- elog (LOG , "searching differential backup..." );
206+ Assert (last_diff_index <= base_index );
207+ /* Tablespace directories checking */
208+ check_tablespace_mapping ((pgBackup * ) parray_get (backups , last_diff_index ));
197209
198- for (i = base_index - 1 ; i >= 0 ; i -- )
210+ /* Restore backups from base_index to last_diff_index */
211+ need_recovery_conf = target_time != NULL || target_xid != NULL ;
212+ for (i = base_index ; i >= last_diff_index ; i -- )
199213 {
200- pgBackup * backup = (pgBackup * ) parray_get (backups , i );
201-
202- /* don't use incomplete nor different timeline backup */
203- if (backup -> status != BACKUP_STATUS_OK ||
204- backup -> tli != base_backup -> tli )
205- continue ;
206-
207- if (backup -> backup_mode == BACKUP_MODE_FULL )
208- break ;
214+ pgBackup * backup = (pgBackup * ) parray_get (backups , i );
209215
210- if (backup_id && backup -> start_time > backup_id )
211- break ;
212-
213- /* use database backup only */
214- if (backup -> backup_mode != BACKUP_MODE_DIFF_PAGE &&
215- backup -> backup_mode != BACKUP_MODE_DIFF_PTRACK )
216- continue ;
217-
218- /* is the backup is necessary for restore to target timeline ? */
219- if (target_tli )
216+ if (backup -> status == BACKUP_STATUS_OK )
220217 {
221- if (!satisfy_timeline (timelines , backup ) ||
222- !satisfy_recovery_target (backup , rt ))
223- continue ;
224- }
225- else
226- if (!satisfy_recovery_target (backup , rt ))
227- continue ;
228-
229- if (backup_id != 0 )
230- stream_wal = backup -> stream ;
218+ need_recovery_conf = need_recovery_conf || !backup -> stream ;
231219
232- print_backup_lsn (backup );
233- restore_database (backup );
220+ print_backup_lsn (backup );
221+ restore_database (backup );
222+ }
234223 }
235224
236225 /* create recovery.conf */
237- if (! stream_wal || target_time != NULL || target_xid != NULL )
226+ if (need_recovery_conf )
238227 create_recovery_conf (backup_id , target_time , target_xid ,
239228 target_inclusive , base_backup -> tli );
240229
@@ -470,9 +459,12 @@ restore_directories(const char *pg_data_dir, const char *backup_dir)
470459 linked_path , dir_created , link_name );
471460 }
472461
473- /* Check if restore destination empty */
462+ /*
463+ * This check was done in check_tablespace_mapping(). But do
464+ * it again.
465+ */
474466 if (!dir_is_empty (linked_path ))
475- elog (ERROR , "restore destination is not empty \"%s\"" ,
467+ elog (ERROR , "restore destination is not empty: \"%s\"" ,
476468 linked_path );
477469
478470 if (link_sep )
@@ -523,6 +515,65 @@ restore_directories(const char *pg_data_dir, const char *backup_dir)
523515 parray_free (dirs );
524516}
525517
518+ /*
519+ * Check that all tablespace mapping entries have correct linked directory
520+ * paths. Linked directories should be empty or do not exist.
521+ *
522+ * If tablespace-mapping option is supplied all OLDDIR entries should have
523+ * entries in tablespace_map file.
524+ */
525+ static void
526+ check_tablespace_mapping (pgBackup * backup )
527+ {
528+ char backup_path [MAXPGPATH ];
529+ parray * links ;
530+ size_t i ;
531+ TablespaceListCell * cell ;
532+
533+ links = parray_new ();
534+
535+ pgBackupGetPath (backup , backup_path , lengthof (backup_path ), NULL );
536+ read_tablespace_map (links , backup_path );
537+
538+ elog (LOG , "check tablespace directories..." );
539+
540+ /* 1 - all linked directories should be empty */
541+ for (i = 0 ; i < parray_num (links ); i ++ )
542+ {
543+ pgFile * link = (pgFile * ) parray_get (links , i );
544+ const char * linked_path = link -> linked ;
545+ TablespaceListCell * cell ;
546+
547+ for (cell = tablespace_dirs .head ; cell ; cell = cell -> next )
548+ if (strcmp (link -> linked , cell -> old_dir ) == 0 )
549+ {
550+ linked_path = cell -> new_dir ;
551+ cell -> checked = true;
552+ break ;
553+ }
554+
555+ if (!is_absolute_path (linked_path ))
556+ elog (ERROR , "tablespace directory is not an absolute path: %s\n" ,
557+ linked_path );
558+
559+ if (!dir_is_empty (linked_path ))
560+ elog (ERROR , "restore destination is not empty: \"%s\"" ,
561+ linked_path );
562+ }
563+
564+ /* 2 - OLDDIR should has an entry in links */
565+ for (cell = tablespace_dirs .head ; cell ; cell = cell -> next )
566+ {
567+ if (!cell -> checked )
568+ elog (ERROR , "--tablespace-mapping option's old directory "
569+ "has not an entry in tablespace_map file: \"%s\"" ,
570+ cell -> old_dir );
571+ }
572+
573+ parray_walk (links , pgBackupFree );
574+ parray_free (links );
575+ }
576+
526577/*
527578 * Restore files into $PGDATA.
528579 */
@@ -998,6 +1049,8 @@ opt_tablespace_map(pgut_option *opt, const char *arg)
9981049 elog (ERROR , "new directory is not an absolute path in tablespace mapping: %s\n" ,
9991050 cell -> new_dir );
10001051
1052+ cell -> checked = false;
1053+
10011054 if (tablespace_dirs .tail )
10021055 tablespace_dirs .tail -> next = cell ;
10031056 else
0 commit comments