X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FLeiMailSync.pm;h=85480599818de3ee1759b7622f35bd789b5fa770;hb=961690bae47c90a4a6960952587c6f4463fb4b19;hp=d93a581042fcd0ae17fc7fc5c265810575b488ea;hpb=42e1fb419b74e0b5c9315dacdd08a0e0c91962e5;p=public-inbox.git diff --git a/lib/PublicInbox/LeiMailSync.pm b/lib/PublicInbox/LeiMailSync.pm index d93a5810..85480599 100644 --- a/lib/PublicInbox/LeiMailSync.pm +++ b/lib/PublicInbox/LeiMailSync.pm @@ -11,9 +11,9 @@ use PublicInbox::ContentHash qw(git_sha); use Carp (); sub dbh_new { - my ($self, $rw) = @_; + my ($self) = @_; my $f = $self->{filename}; - my $creat = $rw && !-s $f; + my $creat = !-s $f; if ($creat) { require PublicInbox::Syscall; open my $fh, '+>>', $f or Carp::croak "open($f): $!"; @@ -23,11 +23,10 @@ sub dbh_new { AutoCommit => 1, RaiseError => 1, PrintError => 0, - ReadOnly => !$rw, sqlite_use_immediate_transaction => 1, }); # no sqlite_unicode, here, all strings are binary - create_tables($self, $dbh) if $rw; + create_tables($self, $dbh); $dbh->do('PRAGMA journal_mode = WAL') if $creat; $dbh->do('PRAGMA case_sensitive_like = ON'); $dbh; @@ -42,7 +41,7 @@ sub new { }, $cls; } -sub lms_write_prepare { ($_[0]->{dbh} //= dbh_new($_[0], 1)); $_[0] } +sub lms_write_prepare { ($_[0]->{dbh} //= dbh_new($_[0])); $_[0] } sub lms_pause { my ($self) = @_; @@ -102,7 +101,7 @@ UPDATE folders SET loc = ? WHERE fid = ? } sub get_fid ($$$) { - my ($sth, $folder, $dbh) = @_; # $dbh is set iff RW + my ($sth, $folder, $dbh) = @_; $sth->bind_param(1, $folder, SQL_BLOB); $sth->execute; my ($fid) = $sth->fetchrow_array; @@ -118,36 +117,37 @@ sub get_fid ($$$) { } sub fid_for { - my ($self, $folder, $rw) = @_; - my $dbh = $self->{dbh} //= dbh_new($self, $rw); + my ($self, $folder, $creat) = @_; + my $dbh = $self->{dbh} //= dbh_new($self); my $sth = $dbh->prepare_cached(<<'', undef, 1); SELECT fid FROM folders WHERE loc = ? LIMIT 1 - my $rw_dbh = $rw ? $dbh : undef; - my $fid = get_fid($sth, $folder, $rw_dbh); + my $fid = get_fid($sth, $folder, $dbh); return $fid if defined($fid); # caller had trailing slash (LeiToMail) if ($folder =~ s!\A((?:maildir|mh):.*?)/+\z!$1!i) { - $fid = get_fid($sth, $folder, $rw_dbh); + $fid = get_fid($sth, $folder, $dbh); if (defined $fid) { - update_fid($dbh, $fid, $folder) if $rw; + update_fid($dbh, $fid, $folder); return $fid; } # sometimes we stored trailing slash.. } elsif ($folder =~ m!\A(?:maildir|mh):!i) { - $fid = get_fid($sth, $folder, $rw_dbh); + $fid = get_fid($sth, $folder, $dbh); if (defined $fid) { - update_fid($dbh, $fid, $folder) if $rw; + update_fid($dbh, $fid, $folder); return $fid; } - } elsif ($rw && $folder =~ m!\Aimaps?://!i) { + } elsif ($creat && $folder =~ m!\Aimaps?://!i) { require PublicInbox::URIimap; - PublicInbox::URIimap->new($folder)->uidvalidity // + my $uri = PublicInbox::URIimap->new($folder); + $uri->uidvalidity // Carp::croak("BUG: $folder has no UIDVALIDITY"); + defined($uri->uid) and Carp::confess("BUG: $folder has UID"); } - return unless $rw; + return unless $creat; ($fid) = $dbh->selectrow_array('SELECT MAX(fid) FROM folders'); $fid += 1; @@ -173,36 +173,60 @@ sub set_src { my ($self, $oidbin, $folder, $id) = @_; my $lk = $self->lock_for_scope; my $fid = $self->{fmap}->{$folder} //= fid_for($self, $folder, 1); - my $sth; + my $dbh = $self->{dbh}; + my ($sth, @param3, $del_old); if (ref($id)) { # scalar name - $id = $$id; - $sth = $self->{dbh}->prepare_cached(<<''); + @param3 = ($$id, SQL_BLOB); + $sth = $dbh->prepare_cached(<<''); INSERT OR IGNORE INTO blob2name (oidbin, fid, name) VALUES (?, ?, ?) + $del_old = $dbh->prepare_cached(<<''); +DELETE FROM blob2name WHERE oidbin = ? AND fid = ? AND name = ? + } else { # numeric ID (IMAP UID, MH number) - $sth = $self->{dbh}->prepare_cached(<<''); + @param3 = ($id); + $sth = $dbh->prepare_cached(<<''); INSERT OR IGNORE INTO blob2num (oidbin, fid, uid) VALUES (?, ?, ?) + $del_old = $dbh->prepare_cached(<<''); +DELETE FROM blob2num WHERE oidbin = ? AND fid = ? AND uid = ? + } - $sth->execute($oidbin, $fid, $id); + $sth->bind_param(1, $oidbin, SQL_BLOB); + $sth->bind_param(2, $fid); + $sth->bind_param(3, @param3); + my $ret = $sth->execute; + $del_old->execute($oidbin, $fid, $param3[0]); + $ret; } sub clear_src { my ($self, $folder, $id) = @_; my $lk = $self->lock_for_scope; my $fid = $self->{fmap}->{$folder} //= fid_for($self, $folder, 1); - my $sth; + my ($sth, @param3); if (ref($id)) { # scalar name - $id = $$id; + @param3 = ($$id, SQL_BLOB); $sth = $self->{dbh}->prepare_cached(<<''); DELETE FROM blob2name WHERE fid = ? AND name = ? } else { + @param3 = ($id); $sth = $self->{dbh}->prepare_cached(<<''); DELETE FROM blob2num WHERE fid = ? AND uid = ? } - $sth->execute($fid, $id); + $sth->bind_param(1, $fid); + $sth->bind_param(2, @param3); + my $ret = $sth->execute; + + # older versions may not have used SQL_BLOB: + if (defined($ret) && $ret == 0 && scalar(@param3) == 2) { + $sth->bind_param(1, $fid); + $sth->bind_param(2, $param3[0]); + $ret = $sth->execute; + } + $ret; } # Maildir-only @@ -215,13 +239,27 @@ sub mv_src { UPDATE blob2name SET name = ? WHERE fid = ? AND oidbin = ? AND name = ? # eval since unique constraint may fail due to race - my $nr = eval { $sth->execute($newbn, $fid, $oidbin, $$id) }; + $sth->bind_param(1, $newbn, SQL_BLOB); + $sth->bind_param(2, $fid); + $sth->bind_param(3, $oidbin, SQL_BLOB); + $sth->bind_param(4, $$id, SQL_BLOB); + my $nr = eval { $sth->execute }; if (!defined($nr) || $nr == 0) { # $nr may be `0E0' + # delete from old, pre-SQL_BLOB rows: + my $del_old = $self->{dbh}->prepare_cached(<<''); +DELETE FROM blob2name WHERE fid = ? AND oidbin = ? AND name = ? + + $del_old->execute($fid, $oidbin, $$id); # missing-OK + $del_old->execute($fid, $oidbin, $newbn); # ditto + # 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); + $sth->bind_param(1, $oidbin, SQL_BLOB); + $sth->bind_param(2, $fid); + $sth->bind_param(3, $newbn, SQL_BLOB); + $sth->execute; } $self->{dbh}->commit; } @@ -301,18 +339,39 @@ SELECT $op(uid) FROM blob2num WHERE fid = ? # returns a { location => [ list-of-ids-or-names ] } mapping sub locations_for { my ($self, $oidbin) = @_; - my ($fid, $sth, $id, %fid2id); + my ($fid, $sth, $id, %fid2id, %seen); my $dbh = $self->{dbh} //= dbh_new($self); $sth = $dbh->prepare('SELECT fid,uid FROM blob2num WHERE oidbin = ?'); + $sth->bind_param(1, $oidbin, SQL_BLOB); + $sth->execute; + while (my ($fid, $uid) = $sth->fetchrow_array) { + push @{$fid2id{$fid}}, $uid; + $seen{"$uid.$fid"} = 1; + } + + # deal with 1.7.0 DBs :< $sth->execute($oidbin); while (my ($fid, $uid) = $sth->fetchrow_array) { + next if $seen{"$uid.$fid"}; push @{$fid2id{$fid}}, $uid; } + + %seen = (); $sth = $dbh->prepare('SELECT fid,name FROM blob2name WHERE oidbin = ?'); + $sth->bind_param(1, $oidbin, SQL_BLOB); + $sth->execute; + while (my ($fid, $name) = $sth->fetchrow_array) { + push @{$fid2id{$fid}}, $name; + $seen{"$fid.$name"} = 1; + } + + # deal with 1.7.0 DBs :< $sth->execute($oidbin); while (my ($fid, $name) = $sth->fetchrow_array) { + next if $seen{"$fid.$name"}; push @{$fid2id{$fid}}, $name; } + $sth = $dbh->prepare('SELECT loc FROM folders WHERE fid = ? LIMIT 1'); my $ret = {}; while (my ($fid, $ids) = each %fid2id) { @@ -355,7 +414,8 @@ SELECT f.loc,b.name FROM blob2name b LEFT JOIN folders f ON b.fid = f.fid WHERE b.oidbin = ? - $b2n->execute(pack('H*', $oidhex)); + $b2n->bind_param(1, pack('H*', $oidhex), SQL_BLOB); + $b2n->execute; while (my ($d, $n) = $b2n->fetchrow_array) { substr($d, 0, length('maildir:')) = ''; # n.b. both mbsync and offlineimap use ":2," as a suffix @@ -572,7 +632,8 @@ sub num_oidbin ($$$) { SELECT oidbin FROM blob2num WHERE fid = ? AND uid = ? ORDER BY _rowid_ EOM $sth->execute($fid, $uid); - map { $_->[0] } @{$sth->fetchall_arrayref}; + my %uniq; # for public-inbox <= 1.7.0 + grep { !$uniq{$_}++ } map { $_->[0] } @{$sth->fetchall_arrayref}; } sub name_oidbin ($$$) { @@ -581,8 +642,14 @@ sub name_oidbin ($$$) { my $sth = $self->{dbh}->prepare_cached(<bind_param(1, $fid); + $sth->bind_param(2, $nm, SQL_BLOB); + $sth->execute; + my @bin = map { $_->[0] } @{$sth->fetchall_arrayref}; $sth->execute($fid, $nm); - map { $_->[0] } @{$sth->fetchall_arrayref}; + my @old = map { $_->[0] } @{$sth->fetchall_arrayref}; + my %uniq; # for public-inbox <= 1.7.0 + grep { !$uniq{$_}++ } (@bin, @old); } sub imap_oidhex {