@@ -113,6 +113,165 @@ do_delete(time_t backup_id)
113
113
parray_free (backup_list );
114
114
}
115
115
116
+ void
117
+ do_detach (time_t backup_id )
118
+ {
119
+ int i ;
120
+ parray * backup_list ,
121
+ * delete_list ;
122
+ pgBackup * target_backup = NULL ;
123
+ size_t size_to_delete = 0 ;
124
+ char size_to_delete_pretty [20 ];
125
+
126
+ /* Get complete list of backups */
127
+ backup_list = catalog_get_backup_list (instance_name , INVALID_BACKUP_ID );
128
+
129
+ delete_list = parray_new ();
130
+
131
+ /* Find backup to be deleted and make increment backups array to be deleted */
132
+ for (i = 0 ; i < parray_num (backup_list ); i ++ )
133
+ {
134
+ pgBackup * backup = (pgBackup * ) parray_get (backup_list , i );
135
+
136
+ if (backup -> start_time == backup_id )
137
+ {
138
+ target_backup = backup ;
139
+ break ;
140
+ }
141
+ }
142
+
143
+ /* sanity */
144
+ if (!target_backup )
145
+ elog (ERROR , "Failed to find backup %s, cannot detach" , base36enc (backup_id ));
146
+
147
+ /* form delete list */
148
+ for (i = 0 ; i < parray_num (backup_list ); i ++ )
149
+ {
150
+ pgBackup * backup = (pgBackup * ) parray_get (backup_list , i );
151
+
152
+ /* check if backup is descendant of delete target */
153
+ if (is_parent (target_backup -> start_time , backup , true))
154
+ {
155
+ parray_append (delete_list , backup );
156
+
157
+ elog (LOG , "Backup %s %s be detached" ,
158
+ base36enc (backup -> start_time ), dry_run ? "can" :"will" );
159
+
160
+ size_to_delete += backup -> data_bytes ;
161
+ if (backup -> stream )
162
+ size_to_delete += backup -> wal_bytes ;
163
+ }
164
+ }
165
+
166
+ /* Report the resident size to delete */
167
+ if (size_to_delete >= 0 )
168
+ {
169
+ pretty_size (size_to_delete , size_to_delete_pretty , lengthof (size_to_delete_pretty ));
170
+ elog (INFO , "Resident data size to free by detach of backup %s : %s" ,
171
+ base36enc (target_backup -> start_time ), size_to_delete_pretty );
172
+ }
173
+
174
+ if (!dry_run )
175
+ {
176
+ /* Lock marked for delete backups */
177
+ catalog_lock_backup_list (delete_list , parray_num (delete_list ) - 1 , 0 , false, true);
178
+
179
+ /* Delete backups from the end of list */
180
+ for (i = (int ) parray_num (delete_list ) - 1 ; i >= 0 ; i -- )
181
+ {
182
+ pgBackup * backup = (pgBackup * ) parray_get (delete_list , (size_t ) i );
183
+
184
+ if (interrupted )
185
+ elog (ERROR , "interrupted during detach backup" );
186
+
187
+ detach_backup_files (backup );
188
+ }
189
+ }
190
+
191
+
192
+ /* cleanup */
193
+ parray_free (delete_list );
194
+ parray_walk (backup_list , pgBackupFree );
195
+ parray_free (backup_list );
196
+ }
197
+
198
+ /*
199
+ * Detach backup files of the backup and update the status of the backup to
200
+ * BACKUP_STATUS_DETACHED.
201
+ * TODO: delete files on multiple threads
202
+ */
203
+ void
204
+ detach_backup_files (pgBackup * backup )
205
+ {
206
+ size_t i ;
207
+ char timestamp [100 ];
208
+ parray * files ;
209
+ size_t num_files ;
210
+ char full_path [MAXPGPATH ];
211
+
212
+ /*
213
+ * If the backup was detached already, there is nothing to do.
214
+ */
215
+ if (backup -> status == BACKUP_STATUS_DETACHED )
216
+ {
217
+ elog (WARNING , "Backup %s already detached" ,
218
+ base36enc (backup -> start_time ));
219
+ return ;
220
+ }
221
+
222
+ if (backup -> recovery_time )
223
+ time2iso (timestamp , lengthof (timestamp ), backup -> recovery_time , false);
224
+ else
225
+ time2iso (timestamp , lengthof (timestamp ), backup -> start_time , false);
226
+
227
+ elog (INFO , "Detach: %s %s" ,
228
+ base36enc (backup -> start_time ), timestamp );
229
+ elog (INFO , "Backup paths:\n root_dir = %s\n database_dir = %s\n" ,
230
+ backup -> root_dir , backup -> database_dir );
231
+
232
+
233
+ /*
234
+ * Update STATUS to BACKUP_STATUS_DETACHING in preparation for the case which
235
+ * the error occurs before deleting all backup files.
236
+ */
237
+ write_backup_status (backup , BACKUP_STATUS_DETACHING , instance_name , false);
238
+ elog (INFO , "Set status in control file: DETACHING" );
239
+
240
+ /* list files to be deleted */
241
+ files = parray_new ();
242
+ dir_list_file (files , backup -> database_dir , false, false, true, false, false, 0 , FIO_BACKUP_HOST );
243
+
244
+ /* delete leaf node first */
245
+ parray_qsort (files , pgFileCompareRelPathWithExternalDesc );
246
+ num_files = parray_num (files );
247
+ for (i = 0 ; i < num_files ; i ++ )
248
+ {
249
+ pgFile * file = (pgFile * ) parray_get (files , i );
250
+
251
+ join_path_components (full_path , backup -> database_dir , file -> rel_path );
252
+
253
+ if (interrupted )
254
+ elog (ERROR , "interrupted during detach backup" );
255
+
256
+ if (progress )
257
+ elog (INFO , "Progress: (%zd/%zd). Delete file \"%s\"" ,
258
+ i + 1 , num_files , full_path );
259
+
260
+ pgFileDelete (file -> mode , full_path );
261
+ }
262
+
263
+ parray_walk (files , pgFileFree );
264
+ parray_free (files );
265
+ backup -> status = BACKUP_STATUS_DETACHED ;
266
+
267
+ /* Update STATUS to BACKUP_STATUS_DETACHED */
268
+ write_backup_status (backup , BACKUP_STATUS_DETACHED , instance_name , true);
269
+ elog (INFO , "Set status in control file: DETACHED" );
270
+
271
+ return ;
272
+ }
273
+
274
+
116
275
/*
117
276
* Merge and purge backups by retention policy. Retention policy is configured by
118
277
* retention_redundancy and retention_window variables.
0 commit comments