@@ -30,6 +30,8 @@ typedef struct TablespaceListCell
30
30
struct TablespaceListCell * next ;
31
31
char old_dir [MAXPGPATH ];
32
32
char new_dir [MAXPGPATH ];
33
+ bool checked ; /* If this mapping was checked during
34
+ restore */
33
35
} TablespaceListCell ;
34
36
35
37
typedef struct TablespaceList
@@ -54,6 +56,7 @@ typedef struct TablespaceCreatedList
54
56
static void restore_database (pgBackup * backup );
55
57
static void restore_directories (const char * pg_data_dir ,
56
58
const char * backup_dir );
59
+ static void check_tablespace_mapping (pgBackup * backup );
57
60
static void create_recovery_conf (time_t backup_id ,
58
61
const char * target_time ,
59
62
const char * target_xid ,
@@ -82,20 +85,25 @@ do_restore(time_t backup_id,
82
85
{
83
86
int i ;
84
87
int base_index ; /* index of base (full) backup */
88
+ int last_diff_index = -1 ; /* index of last differential backup */
85
89
int ret ;
86
90
parray * backups ;
87
91
88
92
parray * timelines ;
89
93
pgBackup * base_backup = NULL ;
90
94
pgBackup * dest_backup = NULL ;
91
95
pgRecoveryTarget * rt = NULL ;
92
- bool backup_id_found = false;
96
+ bool need_recovery_conf = false;
93
97
94
98
/* PGDATA and ARCLOG_PATH are always required */
95
99
if (pgdata == NULL )
96
100
elog (ERROR ,
97
101
"required parameter not specified: PGDATA (-D, --pgdata)" );
98
102
103
+ /* Check if restore destination empty */
104
+ if (!dir_is_empty (pgdata ))
105
+ elog (ERROR , "restore destination is not empty: \"%s\"" , pgdata );
106
+
99
107
elog (LOG , "========================================" );
100
108
elog (LOG , "restore start" );
101
109
@@ -131,110 +139,91 @@ do_restore(time_t backup_id,
131
139
elog (LOG , "searching recent full backup" );
132
140
for (i = 0 ; i < parray_num (backups ); i ++ )
133
141
{
142
+ bool satisfied = false;
143
+
134
144
base_backup = (pgBackup * ) parray_get (backups , i );
135
145
136
146
if (backup_id && base_backup -> start_time > backup_id )
137
147
continue ;
138
148
139
- if (backup_id == base_backup -> start_time &&
140
- base_backup -> status == BACKUP_STATUS_OK )
149
+ if (backup_id == base_backup -> start_time )
141
150
{
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
+
143
156
dest_backup = base_backup ;
144
157
}
145
158
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
-
151
159
if (dest_backup != NULL &&
152
160
base_backup -> backup_mode == BACKUP_MODE_FULL &&
153
161
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 " ,
155
163
base36enc (base_backup -> start_time ),
156
164
base36enc (dest_backup -> start_time ),
157
165
status2str (base_backup -> status ));
158
166
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 ))
161
171
continue ;
162
172
163
173
if (target_tli )
164
174
{
165
175
if (satisfy_timeline (timelines , base_backup ) &&
166
176
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 ;
169
179
}
170
180
else
171
181
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 ;
174
184
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
+ }
176
197
}
177
198
/* no full backup found, cannot restore */
178
- elog (ERROR , "no full backup found, cannot restore. " );
199
+ elog (ERROR , "no full backup found, cannot restore" );
179
200
180
201
base_backup_found :
181
202
base_index = i ;
203
+ if (last_diff_index == -1 )
204
+ last_diff_index = base_index ;
182
205
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 ));
197
209
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 -- )
199
213
{
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 );
209
215
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 )
220
217
{
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 ;
231
219
232
- print_backup_lsn (backup );
233
- restore_database (backup );
220
+ print_backup_lsn (backup );
221
+ restore_database (backup );
222
+ }
234
223
}
235
224
236
225
/* create recovery.conf */
237
- if (! stream_wal || target_time != NULL || target_xid != NULL )
226
+ if (need_recovery_conf )
238
227
create_recovery_conf (backup_id , target_time , target_xid ,
239
228
target_inclusive , base_backup -> tli );
240
229
@@ -470,9 +459,12 @@ restore_directories(const char *pg_data_dir, const char *backup_dir)
470
459
linked_path , dir_created , link_name );
471
460
}
472
461
473
- /* Check if restore destination empty */
462
+ /*
463
+ * This check was done in check_tablespace_mapping(). But do
464
+ * it again.
465
+ */
474
466
if (!dir_is_empty (linked_path ))
475
- elog (ERROR , "restore destination is not empty \"%s\"" ,
467
+ elog (ERROR , "restore destination is not empty: \"%s\"" ,
476
468
linked_path );
477
469
478
470
if (link_sep )
@@ -523,6 +515,65 @@ restore_directories(const char *pg_data_dir, const char *backup_dir)
523
515
parray_free (dirs );
524
516
}
525
517
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
+
526
577
/*
527
578
* Restore files into $PGDATA.
528
579
*/
@@ -998,6 +1049,8 @@ opt_tablespace_map(pgut_option *opt, const char *arg)
998
1049
elog (ERROR , "new directory is not an absolute path in tablespace mapping: %s\n" ,
999
1050
cell -> new_dir );
1000
1051
1052
+ cell -> checked = false;
1053
+
1001
1054
if (tablespace_dirs .tail )
1002
1055
tablespace_dirs .tail -> next = cell ;
1003
1056
else
0 commit comments