X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FLeiMailSync.pm;h=182b0c222412d35ef740353ffb1fd5629d0738aa;hb=14fa0abdcc7b6513540e529375e53edd74ce13e8;hp=f83c7de2c064ab0355f2dda7f07a31255d94d1ed;hpb=387f0e693f9e22a7698927bb40dab0742738e8a6;p=public-inbox.git
diff --git a/lib/PublicInbox/LeiMailSync.pm b/lib/PublicInbox/LeiMailSync.pm
index f83c7de2..182b0c22 100644
--- a/lib/PublicInbox/LeiMailSync.pm
+++ b/lib/PublicInbox/LeiMailSync.pm
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 all contributors
+# Copyright (C) all contributors
# License: AGPL-3.0+
# for maintaining synchronization between lei/store <=> Maildir|MH|IMAP|JMAP
@@ -15,9 +15,9 @@ sub dbh_new {
my $f = $self->{filename};
my $creat = $rw && !-s $f;
if ($creat) {
- require PublicInbox::Spawn;
+ require PublicInbox::Syscall;
open my $fh, '+>>', $f or Carp::croak "open($f): $!";
- PublicInbox::Spawn::nodatacow_fd(fileno($fh));
+ PublicInbox::Syscall::nodatacow_fh($fh);
}
my $dbh = DBI->connect("dbi:SQLite:dbname=$f",'','', {
AutoCommit => 1,
@@ -47,7 +47,8 @@ sub lms_write_prepare { ($_[0]->{dbh} //= dbh_new($_[0], 1)); $_[0] }
sub lms_pause {
my ($self) = @_;
$self->{fmap} = {};
- delete $self->{dbh};
+ my $dbh = delete $self->{dbh};
+ eval { $dbh->do('PRAGMA optimize') } if $dbh;
}
sub create_tables {
@@ -181,25 +182,32 @@ sub mv_src {
my ($self, $folder, $oidbin, $id, $newbn) = @_;
my $lk = $self->lock_for_scope;
my $fid = $self->{fmap}->{$folder} //= fid_for($self, $folder, 1);
+ $self->{dbh}->begin_work;
my $sth = $self->{dbh}->prepare_cached(<<'');
UPDATE blob2name SET name = ? WHERE fid = ? AND oidbin = ? AND name = ?
- my $nr = $sth->execute($newbn, $fid, $oidbin, $$id);
- if ($nr == 0) { # may race with a clear_src, ensure new value exists
+ # eval since unique constraint may fail due to race
+ my $nr = eval { $sth->execute($newbn, $fid, $oidbin, $$id) };
+ if (!defined($nr) || $nr == 0) { # $nr may be `0E0'
+ # may race with a clear_src, ensure new value exists
$sth = $self->{dbh}->prepare_cached(<<'');
INSERT OR IGNORE INTO blob2name (oidbin, fid, name) VALUES (?, ?, ?)
$sth->execute($oidbin, $fid, $newbn);
}
+ $self->{dbh}->commit;
}
# read-only, iterates every oidbin + UID or name for a given folder
sub each_src {
my ($self, $folder, $cb, @args) = @_;
my $dbh = $self->{dbh} //= dbh_new($self);
- my $fid;
+ my ($fid, @rng);
+ my $and_ge_le = '';
if (ref($folder) eq 'HASH') {
$fid = $folder->{fid} // die "BUG: no `fid'";
+ @rng = grep(defined, @$folder{qw(min max)});
+ $and_ge_le = 'AND uid >= ? AND uid <= ?' if @rng;
} else {
$fid = $self->{fmap}->{$folder} //=
fid_for($self, $folder) // return;
@@ -208,16 +216,17 @@ sub each_src {
# minimize implicit txn time to avoid blocking writers by
# batching SELECTs. This looks wonky but is necessary since
# $cb-> may access the DB on its own.
- my $ary = $dbh->selectall_arrayref(<<'', undef, $fid);
-SELECT _rowid_,oidbin,uid FROM blob2num WHERE fid = ?
+ my $ary = $dbh->selectall_arrayref(<<"", undef, $fid, @rng);
+SELECT _rowid_,oidbin,uid FROM blob2num WHERE fid = ? $and_ge_le
ORDER BY _rowid_ ASC LIMIT 1000
my $min = @$ary ? $ary->[-1]->[0] : undef;
while (defined $min) {
for my $row (@$ary) { $cb->($row->[1], $row->[2], @args) }
- $ary = $dbh->selectall_arrayref(<<'', undef, $fid, $min);
-SELECT _rowid_,oidbin,uid FROM blob2num WHERE fid = ? AND _rowid_ > ?
+ $ary = $dbh->selectall_arrayref(<<"", undef, $fid, @rng, $min);
+SELECT _rowid_,oidbin,uid FROM blob2num
+WHERE fid = ? $and_ge_le AND _rowid_ > ?
ORDER BY _rowid_ ASC LIMIT 1000
$min = @$ary ? $ary->[-1]->[0] : undef;
@@ -295,16 +304,19 @@ sub locations_for {
# returns a list of folders used for completion
sub folders {
- my ($self, $pfx) = @_;
- my $dbh = $self->{dbh} //= dbh_new($self);
+ my ($self, @pfx) = @_;
my $sql = 'SELECT loc FROM folders';
- my @pfx;
- if (defined $pfx) {
+ if (defined($pfx[0])) {
$sql .= ' WHERE loc LIKE ? ESCAPE ?';
- @pfx = ($pfx, '\\');
+ my $anywhere = !!$pfx[1];
+ $pfx[1] = '\\';
$pfx[0] =~ s/([%_\\])/\\$1/g; # glob chars
$pfx[0] .= '%';
+ substr($pfx[0], 0, 0, '%') if $anywhere;
+ } else {
+ @pfx = (); # [0] may've been undef
}
+ my $dbh = $self->{dbh} //= dbh_new($self);
map { $_->[0] } @{$dbh->selectall_arrayref($sql, undef, @pfx)};
}
@@ -331,9 +343,12 @@ WHERE b.oidbin = ?
next unless -s $fh;
local $/;
my $raw = <$fh>;
- if ($vrfy && git_sha(1, \$raw)->hexdigest ne $oidhex) {
- warn "$f changed $oidhex\n";
- next;
+ if ($vrfy) {
+ my $got = git_sha(1, \$raw)->hexdigest;
+ if ($got ne $oidhex) {
+ warn "$f changed $oidhex => $got\n";
+ next;
+ }
}
return \$raw;
}
@@ -458,7 +473,7 @@ sub arg2folder {
# using `$res' instead of `$orig'
EOM
} else {
- $lei->err($res) if defined $res;
+ warn($res, "\n") if defined $res;
push @no, $orig;
}
} elsif (m!\A(?:nntps?|s?news)://!i) {
@@ -470,7 +485,7 @@ EOM
# using `$res' instead of `$orig'
EOM
} else {
- $lei->err($res) if defined $res;
+ warn($res, "\n") if defined $res;
push @no, $orig;
}
} else {