+
+ # like copydatabase(1), be sure we don't overwrite anything in case
+ # of other bugs:
+ my $creat = eval($PublicInbox::Search::Xap.'::DB_CREATE()');
+ die if $@;
+ my $XapianWritableDatabase = $PublicInbox::Search::X{WritableDatabase};
+ my $dst = $XapianWritableDatabase->new($tmp, $creat);
+ my $pr = $opt->{-progress};
+ my $pfx = $opt->{-progress_pfx} = progress_pfx($new);
+ my $pr_data = { pr => $pr, pfx => $pfx, nr => 0 } if $pr;
+
+ do {
+ eval {
+ # update the only metadata key for v1:
+ my $lc = $src->get_metadata('last_commit');
+ $dst->set_metadata('last_commit', $lc) if $lc;
+
+ # only the first xapian shard (0) gets 'indexlevel'
+ if ($new =~ m!(?:xapian[0-9]+|xap[0-9]+/0)\b!) {
+ my $l = $src->get_metadata('indexlevel');
+ if ($l eq 'medium') {
+ $dst->set_metadata('indexlevel', $l);
+ }
+ }
+ if ($pr_data) {
+ my $tot = $src->get_doccount;
+
+ # we can only estimate when resharding,
+ # because removed spam causes slight imbalance
+ my $est = '';
+ if (defined $cur_shard && $reshard > 1) {
+ $tot = int($tot/$reshard);
+ $est = 'around ';
+ }
+ my $fmt = "$pfx % ".length($tot)."u/$tot\n";
+ $pr->("$pfx copying $est$tot documents\n");
+ $pr_data->{fmt} = $fmt;
+ $pr_data->{total} = $tot;
+ }
+ };
+ } while (cpdb_retryable($src, $pfx));
+
+ if (defined $reshard) {
+ # we rely on document IDs matching NNTP article number,
+ # so we can't have the Xapian sharding DB support rewriting
+ # document IDs. Thus we iterate through each shard
+ # individually.
+ $src = undef;
+ foreach (@$old) {
+ my $old = $XapianDatabase->new($_);
+ cpdb_loop($old, $dst, $pr_data, $cur_shard, $reshard);