# This program is copyright 2010 Percona Inc. # Feedback and improvements are welcome. # # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation, version 2; OR the Perl Artistic License. On UNIX and similar # systems, you can issue `man perlgpl' or `man perlartistic' to read these # licenses. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 59 Temple # Place, Suite 330, Boston, MA 02111-1307 USA. package delete_more; # This mk-archiver plugin demonstrates how to archive/DELETE rows on one # table--the main table--and also DELETE related rows on other tables. # The picture is: # # main_table-123: other_table-123: # col-m 1 col-o 1 # col-m 2 col-o 1 # col-o 2 # # When rows on main table are deleted, corresponding rows on the other # tables are deleted where main table col-m = other table col-o. This # works for both single and --bulk-delete. The tables are *not* 1-to-1 # so a single delete for main col-m = 1 will result in two deletes fro # other col-o = 1. This means --limit does *not* apply on other table. # # The other table's name is derived from the main table's name according # to the settings below. # # Limitations: # * all tables must be on the same server # * other table column (e.g. opk) must be the same on all other tables # * main table column and other table columns must be numeric # * no NULL values use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); use constant PTDEBUG => $ENV{PTDEBUG}; use Data::Dumper; $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Quotekeys = 0; # ########################################################################### # Customize these values for your tables. # ########################################################################### my $main_table_col = 'id'; # main table pk col my $other_table_col = 'id'; # other table pk col my $main_table_id = qr/(\d+)$/; my $other_table_base = 'other_table-'; my $other_db = undef; # undef = same as main db my $other_table = undef; # undef = auto-determine # ########################################################################### # Don't modify anything below here. # ########################################################################### sub new { my ( $class, %args ) = @_; my $o = $args{OptionParser}; my $q = $args{Quoter}; $other_db ||= $args{db}; if ( !$other_table ) { my ($id) = $args{tbl} =~ m/$main_table_id/; die "Cannot determine other table; $args{tbl} does not match " . $main_table_id unless $id; $other_table = $other_table_base . $id; } $other_table = $q->quote($other_db, $other_table); PTDEBUG && _d('Other table:', $other_table); my $self = { dbh => $args{dbh}, bulk_delete => $o->get('bulk-delete'), limit => $o->get('limit'), delete_rows => [], # saved main table col vals for --bulk-delete main_col_pos => -1, other_tbl => $other_table, }; if ( $o->get('dry-run') ) { print "# delete_more other table $other_table\n"; } return bless $self, $class; } sub before_begin { my ( $self, %args ) = @_; my $allcols = $args{allcols}; PTDEBUG && _d('allcols:', Dumper($allcols)); my $colpos = -1; foreach my $col ( @$allcols ) { $colpos++; last if $col eq $main_table_col; } if ( $colpos < 0 ) { die "Main table column $main_table_col not selected by mk-archiver: " . join(', ', @$allcols); } PTDEBUG && _d('main col pos:', $colpos); $self->{main_col_pos} = $colpos; return; } sub is_archivable { my ( $self, %args ) = @_; my $row = $args{row}; push @{$self->{delete_rows}}, $row->[$self->{main_col_pos}] if $self->{bulk_delete}; return 1; } sub before_delete { my ( $self, %args ) = @_; my $row = $args{row}; my $val = $row->[ $self->{main_col_pos} ]; my $dbh = $self->{dbh}; my $sql = "DELETE FROM $self->{other_tbl} " . "WHERE $other_table_col=$val"; PTDEBUG && _d($sql); eval { $dbh->do($sql); }; if ( $EVAL_ERROR ) { PTDEBUG && _d($EVAL_ERROR); warn $EVAL_ERROR; } return; } sub before_bulk_delete { my ( $self, %args ) = @_; if ( !scalar @{$self->{delete_rows}} ) { warn "before_bulk_delete() called without any rows to delete"; return; } my $dbh = $self->{dbh}; my $delete_rows = join(',', @{$self->{delete_rows}}); $self->{delete_rows} = []; # clear for next call my $sql = "DELETE FROM $self->{other_tbl} " . "WHERE $other_table_col IN ($delete_rows) "; # . "LIMIT $self->{limit}"; PTDEBUG && _d($sql); eval { $dbh->do($sql); }; if ( $EVAL_ERROR ) { PTDEBUG && _d($EVAL_ERROR); warn $EVAL_ERROR; } return; } sub after_finish { my ( $self ) = @_; return; } sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } map { defined $_ ? $_ : 'undef' } @_; print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; } 1;