summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Pritz <bluewind@xinu.at>2019-02-07 16:20:39 +0100
committerFlorian Pritz <bluewind@xinu.at>2019-02-07 17:14:35 +0100
commit138ba53c92dc10c1b9b9bb31448b76f9bbac0ecd (patch)
tree17fb5831b180cb1418a2f5b38d6ce8dd09d28246
parent93af313b49a44b83d13691fe870f8f8fe84e6f92 (diff)
downloadApp-BorgRestore-138ba53c92dc10c1b9b9bb31448b76f9bbac0ecd.tar.gz
App-BorgRestore-138ba53c92dc10c1b9b9bb31448b76f9bbac0ecd.tar.xz
DB/remove_archive: Properly handle cases where the DB is empty after removal
Empty here means that the DB does not contain any backup archive information because all information that is already in the db has to be removed. The old code tries to copy existing data into the new table, but since there are no archives to copy data for, the sql query fails. Also, it would copy all rows and only populate the path column, but if there are no archives that have timestamps, that's actually useless work. This patch ensures that the table is kept empty if there are no archives. Signed-off-by: Florian Pritz <bluewind@xinu.at>
-rw-r--r--Changes2
-rw-r--r--lib/App/BorgRestore/DB.pm20
-rw-r--r--t/handle_added_archives_with_db.t49
3 files changed, 64 insertions, 7 deletions
diff --git a/Changes b/Changes
index dae2435..c90d245 100644
--- a/Changes
+++ b/Changes
@@ -1,6 +1,8 @@
Revision history for Perl extension App-BorgRestore
{{$NEXT}}
+ - Properly handle cases where the DB is empty after removal of archive
+ information
3.2.1 2018-11-01T12:54:26Z
- Add missing version requirement to List::Util dependency
diff --git a/lib/App/BorgRestore/DB.pm b/lib/App/BorgRestore/DB.pm
index 3c9e0fb..fd8506f 100644
--- a/lib/App/BorgRestore/DB.pm
+++ b/lib/App/BorgRestore/DB.pm
@@ -105,21 +105,27 @@ method remove_archive($archive) {
}
my @columns_to_copy = map {'`'._prefix_archive_id($_).'`'} @keep_archives;
+ my @timestamp_columns_to_copy = @columns_to_copy;
@columns_to_copy = ('`path`', @columns_to_copy);
- $self->{dbh}->do('insert into `files_new` select '.join(',', @columns_to_copy).' from files');
+
+ if (@timestamp_columns_to_copy > 0) {
+ $self->{dbh}->do('insert into `files_new` select '.join(',', @columns_to_copy).' from files');
+ }
$self->{dbh}->do('drop table `files`');
$self->{dbh}->do('alter table `files_new` rename to `files`');
- my $sql = 'delete from `files` where ';
- $sql .= join(' is null and ', grep {$_ ne '`path`' } @columns_to_copy);
- $sql .= " is null";
+ if (@timestamp_columns_to_copy > 0) {
+ my $sql = 'delete from `files` where ';
+ $sql .= join(' is null and ', @timestamp_columns_to_copy);
+ $sql .= " is null";
- my $st = $self->{dbh}->prepare($sql);
- my $rows = $st->execute();
+ my $st = $self->{dbh}->prepare($sql);
+ $st->execute();
+ }
- $st = $self->{dbh}->prepare('delete from `archives` where `archive_name` = ?;');
+ my $st = $self->{dbh}->prepare('delete from `archives` where `archive_name` = ?;');
$st->execute($archive);
}
diff --git a/t/handle_added_archives_with_db.t b/t/handle_added_archives_with_db.t
index 2e222b3..6fd94ea 100644
--- a/t/handle_added_archives_with_db.t
+++ b/t/handle_added_archives_with_db.t
@@ -42,6 +42,7 @@ for my $in_memory (0,1) {
$app->_handle_added_archives(['archive-1']);
# check database content
+ is($db->get_archive_row_count(), 15, 'correct row count (15 paths with timestamps)');
eq_or_diff($db->get_archives_for_path('.'), [{archive => 'archive-1', modification_time => undef},]);
eq_or_diff($db->get_archives_for_path('boot'), [{archive => 'archive-1', modification_time => 20},]);
eq_or_diff($db->get_archives_for_path('boot/foo'), [{archive => 'archive-1', modification_time => 19},]);
@@ -85,6 +86,7 @@ for my $in_memory (0,1) {
$app->_handle_added_archives(['archive-2']);
# check database content
+ is($db->get_archive_row_count(), 16, 'correct row count (16 paths with timestamps)');
eq_or_diff($db->get_archives_for_path('.'), [
{archive => 'archive-1', modification_time => undef},
{archive => 'archive-2', modification_time => undef},
@@ -162,6 +164,7 @@ for my $in_memory (0,1) {
$app->_handle_removed_archives(['archive-2']);
# check database contents
+ is($db->get_archive_row_count(), 15, 'correct row count (15 paths with timestamps)');
eq_or_diff($db->get_archives_for_path('.'), [{archive => 'archive-2', modification_time => undef},]);
eq_or_diff($db->get_archives_for_path('boot'), [{archive => 'archive-2', modification_time => 20},]);
eq_or_diff($db->get_archives_for_path('boot/foo'), [{archive => 'archive-2', modification_time => 19},]);
@@ -180,6 +183,52 @@ for my $in_memory (0,1) {
eq_or_diff($db->get_archives_for_path('etc/foo/bar'), [{archive => 'archive-2', modification_time => 1},]);
eq_or_diff($db->get_archives_for_path('etc/foo/blub'), [{archive => 'archive-2', modification_time => undef},]);
eq_or_diff($db->get_archives_for_path('lulz'), [{archive => 'archive-2', modification_time => undef},], 'test non-existant path');
+
+ # run remove again. shouldn't change anything
+ $app->_handle_removed_archives(['archive-2']);
+ is($db->get_archive_row_count(), 15, 'correct row count (15 paths with timestamps)');
+ eq_or_diff($db->get_archives_for_path('.'), [{archive => 'archive-2', modification_time => undef},]);
+ eq_or_diff($db->get_archives_for_path('boot'), [{archive => 'archive-2', modification_time => 20},]);
+ eq_or_diff($db->get_archives_for_path('boot/foo'), [{archive => 'archive-2', modification_time => 19},]);
+ eq_or_diff($db->get_archives_for_path('boot/foo/bar'), [{archive => 'archive-2', modification_time => 19},]);
+ eq_or_diff($db->get_archives_for_path('boot/foo/blub'), [{archive => 'archive-2', modification_time => 13},]);
+ eq_or_diff($db->get_archives_for_path('boot/grub'), [{archive => 'archive-2', modification_time => 20},]);
+ eq_or_diff($db->get_archives_for_path('boot/grub/grub.cfg'), [{archive => 'archive-2', modification_time => 8},]);
+ eq_or_diff($db->get_archives_for_path('boot/test1'), [{archive => 'archive-2', modification_time => 7},]);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f1'), [{archive => 'archive-2', modification_time => 3},]);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f2'), [{archive => 'archive-2', modification_time => 5},]);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f3'), [{archive => 'archive-2', modification_time => 3},]);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f4'), [{archive => 'archive-2', modification_time => 2},]);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f5'), [{archive => 'archive-2', modification_time => 7},]);
+ eq_or_diff($db->get_archives_for_path('etc'), [{archive => 'archive-2', modification_time => 3},]);
+ eq_or_diff($db->get_archives_for_path('etc/foo'), [{archive => 'archive-2', modification_time => 2},]);
+ eq_or_diff($db->get_archives_for_path('etc/foo/bar'), [{archive => 'archive-2', modification_time => 1},]);
+ eq_or_diff($db->get_archives_for_path('etc/foo/blub'), [{archive => 'archive-2', modification_time => undef},]);
+ eq_or_diff($db->get_archives_for_path('lulz'), [{archive => 'archive-2', modification_time => undef},], 'test non-existant path');
+
+ # remove all archives from db (no overlap between archives in db and "current" archives)
+ $app->_handle_removed_archives(['archive-3']);
+
+ # check database contents
+ is($db->get_archive_row_count(), 0, 'db should be empty');
+ eq_or_diff($db->get_archives_for_path('.'), []);
+ eq_or_diff($db->get_archives_for_path('boot'), []);
+ eq_or_diff($db->get_archives_for_path('boot/foo'), []);
+ eq_or_diff($db->get_archives_for_path('boot/foo/bar'), []);
+ eq_or_diff($db->get_archives_for_path('boot/foo/blub'), []);
+ eq_or_diff($db->get_archives_for_path('boot/grub'), []);
+ eq_or_diff($db->get_archives_for_path('boot/grub/grub.cfg'), []);
+ eq_or_diff($db->get_archives_for_path('boot/test1'), []);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f1'), []);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f2'), []);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f3'), []);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f4'), []);
+ eq_or_diff($db->get_archives_for_path('boot/test1/f5'), []);
+ eq_or_diff($db->get_archives_for_path('etc'), []);
+ eq_or_diff($db->get_archives_for_path('etc/foo'), []);
+ eq_or_diff($db->get_archives_for_path('etc/foo/bar'), []);
+ eq_or_diff($db->get_archives_for_path('etc/foo/blub'), []);
+ eq_or_diff($db->get_archives_for_path('lulz'), []);
}