Bug 24153: Remove warning for message queue
[koha.git] / misc / cronjobs / cleanup_database.pl
1 #!/usr/bin/perl
2
3 # Copyright 2009 PTFS, Inc.
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use constant DEFAULT_ZEBRAQ_PURGEDAYS             => 30;
23 use constant DEFAULT_MAIL_PURGEDAYS               => 30;
24 use constant DEFAULT_IMPORT_PURGEDAYS             => 60;
25 use constant DEFAULT_LOGS_PURGEDAYS               => 180;
26 use constant DEFAULT_SEARCHHISTORY_PURGEDAYS      => 30;
27 use constant DEFAULT_SHARE_INVITATION_EXPIRY_DAYS => 14;
28 use constant DEFAULT_DEBARMENTS_PURGEDAYS         => 30;
29
30 BEGIN {
31     # find Koha's Perl modules
32     # test carefully before changing this
33     use FindBin;
34     eval { require "$FindBin::Bin/../kohalib.pl" };
35 }
36
37 use Koha::Script -cron;
38 use C4::Context;
39 use C4::Search;
40 use C4::Search::History;
41 use Getopt::Long;
42 use C4::Log;
43 use C4::Accounts;
44 use Koha::UploadedFiles;
45 use Koha::Old::Biblios;
46 use Koha::Old::Items;
47 use Koha::Old::Biblioitems;
48 use Koha::Old::Checkouts;
49 use Koha::Old::Holds;
50 use Koha::Old::Patrons;
51 use Koha::Item::Transfers;
52 use Koha::PseudonymizedTransactions;
53
54 sub usage {
55     print STDERR <<USAGE;
56 Usage: $0 [-h|--help] [--confirm] [--sessions] [--sessdays DAYS] [-v|--verbose] [--zebraqueue DAYS] [-m|--mail] [--merged] [--import DAYS] [--logs DAYS] [--searchhistory DAYS] [--restrictions DAYS] [--all-restrictions] [--fees DAYS] [--temp-uploads] [--temp-uploads-days DAYS] [--uploads-missing 0|1 ] [--statistics DAYS] [--deleted-catalog DAYS] [--deleted-patrons DAYS] [--old-issues DAYS] [--old-reserves DAYS] [--transfers DAYS]
57
58    -h --help          prints this help message, and exits, ignoring all
59                       other options
60    --confirm          Confirmation flag, the script will be running in dry-run mode is not set.
61    --sessions         purge the sessions table.  If you use this while users 
62                       are logged into Koha, they will have to reconnect.
63    --sessdays DAYS    purge only sessions older than DAYS days.
64    -v --verbose       will cause the script to give you a bit more information
65                       about the run.
66    --zebraqueue DAYS  purge completed zebraqueue entries older than DAYS days.
67                       Defaults to 30 days if no days specified.
68    -m --mail DAYS     purge items from the mail queue that are older than DAYS days.
69                       Defaults to 30 days if no days specified.
70    --merged           purged completed entries from need_merge_authorities.
71    --import DAYS      purge records from import tables older than DAYS days.
72                       Defaults to 60 days if no days specified.
73    --z3950            purge records from import tables that are the result
74                       of Z39.50 searches
75    --fees DAYS        purge entries accountlines older than DAYS days, where
76                       amountoutstanding is 0 or NULL.
77                       In the case of --fees, DAYS must be greater than
78                       or equal to 1.
79    --logs DAYS        purge entries from action_logs older than DAYS days.
80                       Defaults to 180 days if no days specified.
81    --searchhistory DAYS  purge entries from search_history older than DAYS days.
82                          Defaults to 30 days if no days specified
83    --list-invites  DAYS  purge (unaccepted) list share invites older than DAYS
84                          days.  Defaults to 14 days if no days specified.
85    --restrictions DAYS   purge patrons restrictions expired since more than DAYS days.
86                          Defaults to 30 days if no days specified.
87     --all-restrictions   purge all expired patrons restrictions.
88    --del-exp-selfreg  Delete expired self registration accounts
89    --del-unv-selfreg  DAYS  Delete unverified self registrations older than DAYS
90    --unique-holidays DAYS  Delete all unique holidays older than DAYS
91    --temp-uploads     Delete temporary uploads.
92    --temp-uploads-days DAYS Override the corresponding preference value.
93    --uploads-missing FLAG Delete upload records for missing files when FLAG is true, count them otherwise
94    --oauth-tokens     Delete expired OAuth2 tokens
95    --statistics DAYS       Purge statistics entries more than DAYS days old.
96                            This table is used to build reports, make sure you are aware of the consequences of this before using it!
97    --deleted-catalog  DAYS Purge catalog records deleted more then DAYS days ago
98                            (from tables deleteditems, deletedbiblioitems, deletedbiblio_metadata and deletedbiblio).
99    --deleted-patrons DAYS  Purge patrons deleted more than DAYS days ago.
100    --old-issues DAYS       Purge checkouts (old_issues) returned more than DAYS days ago.
101    --old-reserves DAYS     Purge reserves (old_reserves) more than DAYS old.
102    --transfers DAYS        Purge transfers completed more than DAYS day ago.
103    --pseudo-transactions DAYS   Purge the pseudonymized transactions that have been originally created more than DAYS days ago
104                                 DAYS is optional and can be replaced by:
105                                     --pseudo-transactions-from YYYY-MM-DD and/or --pseudo-transactions-to YYYY-MM-DD
106 USAGE
107     exit $_[0];
108 }
109
110 my $help;
111 my $confirm;
112 my $sessions;
113 my $sess_days;
114 my $verbose;
115 my $zebraqueue_days;
116 my $mail;
117 my $purge_merged;
118 my $pImport;
119 my $pLogs;
120 my $pSearchhistory;
121 my $pZ3950;
122 my $pListShareInvites;
123 my $pDebarments;
124 my $allDebarments;
125 my $pExpSelfReg;
126 my $pUnvSelfReg;
127 my $fees_days;
128 my $special_holidays_days;
129 my $temp_uploads;
130 my $temp_uploads_days;
131 my $uploads_missing;
132 my $oauth_tokens;
133 my $pStatistics;
134 my $pDeletedCatalog;
135 my $pDeletedPatrons;
136 my $pOldIssues;
137 my $pOldReserves;
138 my $pTransfers;
139 my ( $pPseudoTransactions, $pPseudoTransactionsFrom, $pPseudoTransactionsTo );
140
141 GetOptions(
142     'h|help'            => \$help,
143     'confirm'           => \$confirm,
144     'sessions'          => \$sessions,
145     'sessdays:i'        => \$sess_days,
146     'v|verbose'         => \$verbose,
147     'm|mail:i'          => \$mail,
148     'zebraqueue:i'      => \$zebraqueue_days,
149     'merged'            => \$purge_merged,
150     'import:i'          => \$pImport,
151     'z3950'             => \$pZ3950,
152     'logs:i'            => \$pLogs,
153     'fees:i'            => \$fees_days,
154     'searchhistory:i'   => \$pSearchhistory,
155     'list-invites:i'    => \$pListShareInvites,
156     'restrictions:i'    => \$pDebarments,
157     'all-restrictions'  => \$allDebarments,
158     'del-exp-selfreg'   => \$pExpSelfReg,
159     'del-unv-selfreg'   => \$pUnvSelfReg,
160     'unique-holidays:i' => \$special_holidays_days,
161     'temp-uploads'      => \$temp_uploads,
162     'temp-uploads-days:i' => \$temp_uploads_days,
163     'uploads-missing:i' => \$uploads_missing,
164     'oauth-tokens'      => \$oauth_tokens,
165     'statistics:i'      => \$pStatistics,
166     'deleted-catalog:i' => \$pDeletedCatalog,
167     'deleted-patrons:i' => \$pDeletedPatrons,
168     'old-issues:i'      => \$pOldIssues,
169     'old-reserves:i'    => \$pOldReserves,
170     'transfers:i'       => \$pTransfers,
171     'pseudo-transactions:i'      => \$pPseudoTransactions,
172     'pseudo-transactions-from:s' => \$pPseudoTransactionsFrom,
173     'pseudo-transactions-to:s'   => \$pPseudoTransactionsTo,
174 ) || usage(1);
175
176 # Use default values
177 $sessions          = 1                                    if $sess_days                  && $sess_days > 0;
178 $pImport           = DEFAULT_IMPORT_PURGEDAYS             if defined($pImport)           && $pImport == 0;
179 $pLogs             = DEFAULT_LOGS_PURGEDAYS               if defined($pLogs)             && $pLogs == 0;
180 $zebraqueue_days   = DEFAULT_ZEBRAQ_PURGEDAYS             if defined($zebraqueue_days)   && $zebraqueue_days == 0;
181 $mail              = DEFAULT_MAIL_PURGEDAYS               if defined($mail)              && $mail == 0;
182 $pSearchhistory    = DEFAULT_SEARCHHISTORY_PURGEDAYS      if defined($pSearchhistory)    && $pSearchhistory == 0;
183 $pListShareInvites = DEFAULT_SHARE_INVITATION_EXPIRY_DAYS if defined($pListShareInvites) && $pListShareInvites == 0;
184 $pDebarments       = DEFAULT_DEBARMENTS_PURGEDAYS         if defined($pDebarments)       && $pDebarments == 0;
185
186 if ($help) {
187     usage(0);
188 }
189
190 unless ( $sessions
191     || $zebraqueue_days
192     || $mail
193     || $purge_merged
194     || $pImport
195     || $pLogs
196     || $fees_days
197     || $pSearchhistory
198     || $pZ3950
199     || $pListShareInvites
200     || $pDebarments
201     || $allDebarments
202     || $pExpSelfReg
203     || $pUnvSelfReg
204     || $special_holidays_days
205     || $temp_uploads
206     || defined $uploads_missing
207     || $oauth_tokens
208     || $pStatistics
209     || $pDeletedCatalog
210     || $pDeletedPatrons
211     || $pOldIssues
212     || $pOldReserves
213     || $pTransfers
214     || defined $pPseudoTransactions
215     || $pPseudoTransactionsFrom
216     || $pPseudoTransactionsTo
217 ) {
218     print "You did not specify any cleanup work for the script to do.\n\n";
219     usage(1);
220 }
221
222 if ($pDebarments && $allDebarments) {
223     print "You can not specify both --restrictions and --all-restrictions.\n\n";
224     usage(1);
225 }
226
227 say "Confirm flag not passed, running in dry-run mode..." unless $confirm;
228
229 cronlogaction() unless $confirm;
230
231 my $dbh = C4::Context->dbh();
232 my $sth;
233 my $sth2;
234 my $count;
235
236 if ( $sessions && !$sess_days ) {
237     if ($verbose) {
238         print "Session purge triggered.\n";
239         $sth = $dbh->prepare(q{ SELECT COUNT(*) FROM sessions });
240         $sth->execute() or die $dbh->errstr;
241         my @count_arr = $sth->fetchrow_array;
242         print "$count_arr[0] entries will be deleted.\n";
243     }
244     if ( $confirm ) {
245         $sth = $dbh->prepare(q{ TRUNCATE sessions });
246         $sth->execute() or die $dbh->errstr;
247     }
248     if ($verbose) {
249         print "Done with session purge.\n";
250     }
251 }
252 elsif ( $sessions && $sess_days > 0 ) {
253     print "Session purge triggered with days>$sess_days.\n" if $verbose;
254     RemoveOldSessions() if $confirm;
255     print "Done with session purge with days>$sess_days.\n" if $verbose;
256 }
257
258 if ($zebraqueue_days) {
259     $count = 0;
260     print "Zebraqueue purge triggered for $zebraqueue_days days.\n" if $verbose;
261     $sth = $dbh->prepare(
262         q{
263             SELECT id,biblio_auth_number,server,time
264             FROM zebraqueue
265             WHERE done=1 AND time < date_sub(curdate(), INTERVAL ? DAY)
266         }
267     );
268     if ( $confirm ) {
269         $sth->execute($zebraqueue_days) or die $dbh->errstr;
270     }
271     $sth2 = $dbh->prepare(q{ DELETE FROM zebraqueue WHERE id=? });
272     while ( my $record = $sth->fetchrow_hashref ) {
273         if ( $confirm ) {
274             $sth2->execute( $record->{id} ) or die $dbh->errstr;
275         }
276         $count++;
277     }
278     print "$count records were deleted.\nDone with zebraqueue purge.\n" if $verbose;
279 }
280
281 if ($mail) {
282     print "Mail queue purge triggered for $mail days.\n" if $verbose;
283     $count = 0;
284     $sth = $dbh->prepare(
285         q{
286             DELETE FROM message_queue
287             WHERE time_queued < date_sub(curdate(), INTERVAL ? DAY)
288         }
289     );
290     if ( $confirm ) {
291         $sth->execute($mail) or die $dbh->errstr;
292         $count = $sth->rows;
293         print "$count messages were deleted from the mail queue.\nDone with message_queue purge.\n" if $verbose;
294     } else {
295         print "Messages were deleted from the mail queue.\nDone with message_queue purge.\n" if $verbose;
296     }
297 }
298
299 if ($purge_merged) {
300     print "Purging completed entries from need_merge_authorities.\n" if $verbose;
301     if ( $confirm ) {
302         $sth = $dbh->prepare(q{ DELETE FROM need_merge_authorities WHERE done=1 });
303         $sth->execute() or die $dbh->errstr;
304     }
305     print "Done with purging need_merge_authorities.\n" if $verbose;
306 }
307
308 if ($pImport) {
309     print "Purging records from import tables.\n" if $verbose;
310     PurgeImportTables() if $confirm;
311     print "Done with purging import tables.\n" if $verbose;
312 }
313
314 if ($pZ3950) {
315     print "Purging Z39.50 records from import tables.\n" if $verbose;
316     PurgeZ3950() if $confirm;
317     print "Done with purging Z39.50 records from import tables.\n" if $verbose;
318 }
319
320 if ($pLogs) {
321     print "Purging records from action_logs.\n" if $verbose;
322     $sth = $dbh->prepare(
323         q{
324             DELETE FROM action_logs
325             WHERE timestamp < date_sub(curdate(), INTERVAL ? DAY)
326         }
327     );
328     if ( $confirm ) {
329         $sth->execute($pLogs) or die $dbh->errstr;
330     }
331     print "Done with purging action_logs.\n" if $verbose;
332 }
333
334 if ($fees_days) {
335     print "Purging records from accountlines.\n" if $verbose;
336     purge_zero_balance_fees( $fees_days ) if $confirm;
337     print "Done purging records from accountlines.\n" if $verbose;
338 }
339
340 if ($pSearchhistory) {
341     print "Purging records older than $pSearchhistory from search_history.\n" if $verbose;
342     C4::Search::History::delete({ interval => $pSearchhistory }) if $confirm;
343     print "Done with purging search_history.\n" if $verbose;
344 }
345
346 if ($pListShareInvites) {
347     print "Purging unaccepted list share invites older than $pListShareInvites days.\n" if $verbose;
348     $sth = $dbh->prepare(
349         q{
350             DELETE FROM virtualshelfshares
351             WHERE invitekey IS NOT NULL
352             AND (sharedate + INTERVAL ? DAY) < NOW()
353         }
354     );
355     if ( $confirm ) {
356         $sth->execute($pListShareInvites);
357     }
358     print "Done with purging unaccepted list share invites.\n" if $verbose;
359 }
360
361 if ($pDebarments) {
362     print "Expired patrons restrictions purge triggered for $pDebarments days.\n" if $verbose;
363     $count = PurgeDebarments($pDebarments, $confirm);
364     print "$count restrictions were deleted.\nDone with restrictions purge.\n" if $verbose;
365 }
366
367 if($allDebarments) {
368     print "All expired patrons restrictions purge triggered.\n" if $verbose;
369     $count = PurgeDebarments(0, $confirm);
370     print "$count restrictions were deleted.\nDone with all restrictions purge.\n" if $verbose;
371 }
372
373 # Handle unsubscribe requests from GDPR consent form, depends on UnsubscribeReflectionDelay preference
374 my $unsubscribed_patrons = Koha::Patrons->search_unsubscribed;
375 $count = $unsubscribed_patrons->count;
376 $unsubscribed_patrons->lock( { expire => 1, remove => 1 } ) if $confirm;
377 say sprintf "Locked %d patrons", $count if $verbose;
378
379 # Anonymize patron data, depending on PatronAnonymizeDelay
380 my $anonymize_candidates = Koha::Patrons->search_anonymize_candidates( { locked => 1 } );
381 $count = $anonymize_candidates->count;
382 $anonymize_candidates->anonymize if $confirm;
383 say sprintf "Anonymized %s patrons", $count if $verbose;
384
385 # Remove patron data, depending on PatronRemovalDelay (will raise an exception if problem encountered
386 my $anonymized_patrons = Koha::Patrons->search_anonymized;
387 $count = $anonymized_patrons->count;
388 if ( $confirm ) {
389     $anonymized_patrons->delete( { move => 1 } );
390     if ($@) {
391         warn $@;
392     }
393     elsif ($verbose) {
394         say sprintf "Deleted %d patrons", $count;
395     }
396 } else {
397     say sprintf "Deleted %d patrons", $count;
398 }
399
400 # FIXME The output for dry-run mode needs to be improved
401 # But non trivial changes to C4::Members need to be done before.
402 if( $pExpSelfReg ) {
403     if ( $confirm ) {
404         DeleteExpiredSelfRegs();
405     } elsif ( $verbose ) {
406         say "self-registered borrowers may be deleted";
407     }
408 }
409 if( $pUnvSelfReg ) {
410     if ( $confirm ) {
411         DeleteUnverifiedSelfRegs( $pUnvSelfReg );
412     } elsif ( $verbose ) {
413         say "unverified self-registrations may be deleted";
414     }
415 }
416
417 if ($special_holidays_days) {
418     if ( $confirm ) {
419         DeleteSpecialHolidays( abs($special_holidays_days) );
420     } elsif ( $verbose ) {
421         say "self-registered borrowers may be deleted";
422     }
423 }
424
425 if( $temp_uploads ) {
426     # Delete temporary uploads, governed by a pref (unless you override)
427     print "Purging temporary uploads.\n" if $verbose;
428     if ( $confirm ) {
429         Koha::UploadedFiles->delete_temporary({
430             defined($temp_uploads_days)
431                 ? ( override_pref => $temp_uploads_days )
432                 : ()
433         });
434     }
435     print "Done purging temporary uploads.\n" if $verbose;
436 }
437
438 if( defined $uploads_missing ) {
439     print "Looking for missing uploads\n" if $verbose;
440     if ( $confirm ) {
441         my $keep = $uploads_missing == 1 ? 0 : 1;
442         my $count = Koha::UploadedFiles->delete_missing({ keep_record => $keep });
443         if( $keep ) {
444             print "Counted $count missing uploaded files\n";
445         } else {
446             print "Removed $count records for missing uploads\n";
447         }
448     } else {
449         # FIXME need to create a filter_by_missing method (then call ->delete) instead of delete_missing
450         say "Dry-run mode cannot guess how many uploads would have been deleted";
451     }
452 }
453
454 if ($oauth_tokens) {
455     require Koha::OAuthAccessTokens;
456
457     my $tokens = Koha::OAuthAccessTokens->search({ expires => { '<=', time } });
458     my $count = $tokens->count;
459     $tokens->delete if $confirm;
460     say sprintf "Removed %s expired OAuth2 tokens", $count if $verbose;
461 }
462
463 if ($pStatistics) {
464     print "Purging statistics older than $pStatistics days.\n" if $verbose;
465     my $statistics = Koha::Statistics->filter_by_last_update(
466         { timestamp_column_name => 'datetime', days => $pStatistics } );
467     my $count = $statistics->count;
468     $statistics->delete if $confirm;
469     say sprintf "Done with purging %s statistics.", $count if $verbose;
470 }
471
472 if ($pDeletedCatalog) {
473     print "Purging deleted catalog older than $pDeletedCatalog days.\n"
474       if $verbose;
475     my $old_items = Koha::Old::Items->filter_by_last_update( { days => $pDeletedCatalog } );
476     my $old_biblioitems = Koha::Old::Biblioitems->filter_by_last_update( { days => $pDeletedCatalog } );
477     my $old_biblios = Koha::Old::Biblios->filter_by_last_update( { days => $pDeletedCatalog } );
478     my ( $c_i, $c_bi, $c_b ) =
479       ( $old_items->count, $old_biblioitems->count, $old_biblios->count );
480     if ($confirm) {
481         $old_items->delete;
482         $old_biblioitems->delete;
483         $old_biblios->delete;
484     }
485     say sprintf
486         "Done with purging deleted catalog (%d items, %d biblioitems, %d biblios).",
487       $c_i, $c_bi, $c_b
488       if $verbose;
489 }
490
491 if ($pDeletedPatrons) {
492     print "Purging deleted patrons older than $pDeletedPatrons days.\n" if $verbose;
493     my $old_patrons = Koha::Old::Patrons->filter_by_last_update(
494         { timestamp_column_name => 'updated_on', days => $pDeletedPatrons } );
495     my $count = $old_patrons->count;
496     $old_patrons->delete if $confirm;
497     say sprintf "Done with purging %d deleted patrons.", $count if $verbose;
498 }
499
500 if ($pOldIssues) {
501     print "Purging old checkouts older than $pOldIssues days.\n" if $verbose;
502     my $old_checkouts = Koha::Old::Checkouts->filter_by_last_update( { days => $pOldIssues } );
503     my $count = $old_checkouts->count;
504     $old_checkouts->delete if $confirm;
505     say sprintf "Done with purging %d old checkouts.", $count if $verbose;
506 }
507
508 if ($pOldReserves) {
509     print "Purging old reserves older than $pOldReserves days.\n" if $verbose;
510     my $old_reserves = Koha::Old::Holds->filter_by_last_update( { days => $pOldReserves } );
511     my $count = $old_reserves->count;
512     $old_reserves->delete if $verbose;
513     say sprintf "Done with purging %d old reserves.", $count if $verbose;
514 }
515
516 if ($pTransfers) {
517     print "Purging arrived item transfers older than $pTransfers days.\n" if $verbose;
518     my $transfers = Koha::Item::Transfers->filter_by_last_update(
519         {
520             timestamp_column_name => 'datearrived',
521             days => $pTransfers,
522         }
523     );
524     my $count = $transfers->count;
525     $transfers->delete if $verbose;
526     say sprintf "Done with purging %d transfers.", $count if $verbose;
527 }
528
529 if (defined $pPseudoTransactions or $pPseudoTransactionsFrom or $pPseudoTransactionsTo ) {
530     print "Purging pseudonymized transactions\n" if $verbose;
531     my $anonymized_transactions = Koha::PseudonymizedTransactions->filter_by_last_update(
532         {
533             timestamp_column_name => 'datetime',
534             ( defined $pPseudoTransactions  ? ( days => $pPseudoTransactions     ) : () ),
535             ( $pPseudoTransactionsFrom      ? ( from => $pPseudoTransactionsFrom ) : () ),
536             ( $pPseudoTransactionsTo        ? ( to   => $pPseudoTransactionsTo   ) : () ),
537         }
538     );
539     my $count = $anonymized_transactions->count;
540     $anonymized_transactions->delete if $confirm;
541     say sprintf "Done with purging %d pseudonymized transactions.", $count if $verbose;
542 }
543
544 exit(0);
545
546 sub RemoveOldSessions {
547     my ( $id, $a_session, $limit, $lasttime );
548     $limit = time() - 24 * 3600 * $sess_days;
549
550     $sth = $dbh->prepare(q{ SELECT id, a_session FROM sessions });
551     $sth->execute or die $dbh->errstr;
552     $sth->bind_columns( \$id, \$a_session );
553     $sth2  = $dbh->prepare(q{ DELETE FROM sessions WHERE id=? });
554     $count = 0;
555
556     while ( $sth->fetch ) {
557         $lasttime = 0;
558         if ( $a_session =~ /lasttime:\s+'?(\d+)/ ) {
559             $lasttime = $1;
560         }
561         elsif ( $a_session =~ /(ATIME|CTIME):\s+'?(\d+)/ ) {
562             $lasttime = $2;
563         }
564         if ( $lasttime && $lasttime < $limit ) {
565             $sth2->execute($id) or die $dbh->errstr;
566             $count++;
567         }
568     }
569     if ($verbose) {
570         print "$count sessions were deleted.\n";
571     }
572 }
573
574 sub PurgeImportTables {
575
576     #First purge import_records
577     #Delete cascades to import_biblios, import_items and import_record_matches
578     $sth = $dbh->prepare(
579         q{
580             DELETE FROM import_records
581             WHERE upload_timestamp < date_sub(curdate(), INTERVAL ? DAY)
582         }
583     );
584     $sth->execute($pImport) or die $dbh->errstr;
585
586     # Now purge import_batches
587     # Timestamp cannot be used here without care, because records are added
588     # continuously to batches without updating timestamp (Z39.50 search).
589     # So we only delete older empty batches.
590     # This delete will therefore not have a cascading effect.
591     $sth = $dbh->prepare(
592         q{
593             DELETE ba
594             FROM import_batches ba
595             LEFT JOIN import_records re ON re.import_batch_id=ba.import_batch_id
596             WHERE re.import_record_id IS NULL AND
597             ba.upload_timestamp < date_sub(curdate(), INTERVAL ? DAY)
598         }
599     );
600     $sth->execute($pImport) or die $dbh->errstr;
601 }
602
603 sub PurgeZ3950 {
604     $sth = $dbh->prepare(
605         q{
606             DELETE FROM import_batches
607             WHERE batch_type = 'z3950'
608         }
609     );
610     $sth->execute() or die $dbh->errstr;
611 }
612
613 sub PurgeDebarments {
614     require Koha::Patron::Debarments;
615     my ( $days, $doit ) = @_;
616     $count = 0;
617     $sth   = $dbh->prepare(
618         q{
619             SELECT borrower_debarment_id
620             FROM borrower_debarments
621             WHERE expiration < date_sub(curdate(), INTERVAL ? DAY)
622         }
623     );
624     $sth->execute($days) or die $dbh->errstr;
625     while ( my ($borrower_debarment_id) = $sth->fetchrow_array ) {
626         Koha::Patron::Debarments::DelDebarment($borrower_debarment_id) if $doit;
627         $count++;
628     }
629     return $count;
630 }
631
632 sub DeleteExpiredSelfRegs {
633     my $cnt= C4::Members::DeleteExpiredOpacRegistrations();
634     print "Removed $cnt expired self-registered borrowers\n" if $verbose;
635 }
636
637 sub DeleteUnverifiedSelfRegs {
638     my $cnt= C4::Members::DeleteUnverifiedOpacRegistrations( $_[0] );
639     print "Removed $cnt unverified self-registrations\n" if $verbose;
640 }
641
642 sub DeleteSpecialHolidays {
643     my ( $days ) = @_;
644
645     my $sth = $dbh->prepare(q{
646         DELETE FROM special_holidays
647         WHERE DATE( CONCAT( year, '-', month, '-', day ) ) < DATE_SUB( CAST(NOW() AS DATE), INTERVAL ? DAY );
648     });
649     my $count = $sth->execute( $days ) + 0;
650     print "Removed $count unique holidays\n" if $verbose;
651 }