mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-07 12:58:45 +00:00
Merge pull request #92 from ManjotS/2.2
Added --preserve-triggers to pt-osc
This commit is contained in:
@@ -2253,6 +2253,7 @@ sub get_dbh {
|
||||
PrintError => 0,
|
||||
ShowErrorStatement => 1,
|
||||
mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/i ? 1 : 0),
|
||||
mysql_multi_statements => 1,
|
||||
};
|
||||
@{$defaults}{ keys %$opts } = values %$opts;
|
||||
if (delete $defaults->{L}) { # L for LOAD DATA LOCAL INFILE, our own extension
|
||||
@@ -8029,6 +8030,7 @@ my $dont_interrupt_now = 0;
|
||||
my @drop_trigger_sqls;
|
||||
my @triggers_not_dropped;
|
||||
my $pxc_version = '0';
|
||||
my @orig_triggers = ('','','');
|
||||
# Completely ignore these error codes.
|
||||
my %ignore_code = (
|
||||
# Error: 1592 SQLSTATE: HY000 (ER_BINLOG_UNSAFE_STATEMENT)
|
||||
@@ -9545,6 +9547,20 @@ sub main {
|
||||
$plugin->before_swap_tables();
|
||||
}
|
||||
|
||||
if ( $o->get('preserve-triggers') ) {
|
||||
print "Adding original triggers to new table.\n";
|
||||
|
||||
foreach my $orig_trigger (@orig_triggers) {
|
||||
if ($orig_trigger ne '') {
|
||||
PTDEBUG && _d($orig_trigger);
|
||||
print $orig_trigger, "\n" if $o->get('print');
|
||||
if ( $o->get('execute') ) {
|
||||
$cxn->dbh()->do($orig_trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $old_tbl;
|
||||
if ( $o->get('swap-tables') ) {
|
||||
|
||||
@@ -10210,7 +10226,7 @@ sub check_orig_table {
|
||||
. ' LIKE ' . $q->literal_like($orig_tbl->{tbl});
|
||||
PTDEBUG && _d($sql);
|
||||
my $triggers = $dbh->selectall_arrayref($sql);
|
||||
if ( $triggers && @$triggers ) {
|
||||
if ( $triggers && @$triggers && !$o->get('preserve-triggers')) {
|
||||
die "The table $orig_tbl->{name} has triggers. This tool "
|
||||
. "needs to create its own triggers, so the table cannot "
|
||||
. "already have triggers.\n";
|
||||
@@ -10564,22 +10580,187 @@ sub create_triggers {
|
||||
"$new_tbl->{name}.$new_qcol <=> OLD.$old_qcol"
|
||||
} @{$tbl_struct->{keys}->{$del_index}->{cols}} );
|
||||
|
||||
my $delete_trigger
|
||||
= "CREATE TRIGGER `${prefix}_del` AFTER DELETE ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW "
|
||||
. "DELETE IGNORE FROM $new_tbl->{name} "
|
||||
. "WHERE $del_index_cols";
|
||||
my $delete_trigger;
|
||||
my $insert_trigger;
|
||||
my $update_trigger;
|
||||
|
||||
my $qcols = join(', ', map { $q->quote($_->{new}) } @$cols);
|
||||
my $new_vals = join(', ', map { "NEW.".$q->quote($_->{old}) } @$cols);
|
||||
my $insert_trigger
|
||||
= "CREATE TRIGGER `${prefix}_ins` AFTER INSERT ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW "
|
||||
. "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";
|
||||
my $update_trigger
|
||||
= "CREATE TRIGGER `${prefix}_upd` AFTER UPDATE ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW "
|
||||
. "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";
|
||||
|
||||
|
||||
my $orig_delete_trigger;
|
||||
my $orig_insert_trigger;
|
||||
my $orig_update_trigger;
|
||||
|
||||
|
||||
if ( $o->get('preserve-triggers') ) {
|
||||
$cxn->connect();
|
||||
|
||||
my $dbh = $cxn->dbh();
|
||||
|
||||
$orig_delete_trigger = $dbh->selectrow_arrayref("SELECT TRIGGER_SCHEMA,TRIGGER_NAME,DEFINER,ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS WHERE EVENT_MANIPULATION='DELETE' AND ACTION_TIMING='AFTER' AND TRIGGER_SCHEMA='$orig_tbl->{db}' AND EVENT_OBJECT_TABLE='$orig_tbl->{tbl}';");
|
||||
$orig_insert_trigger = $dbh->selectrow_arrayref("SELECT TRIGGER_SCHEMA,TRIGGER_NAME,DEFINER,ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS WHERE EVENT_MANIPULATION='INSERT' AND ACTION_TIMING='AFTER' AND TRIGGER_SCHEMA='$orig_tbl->{db}' AND EVENT_OBJECT_TABLE='$orig_tbl->{tbl}';");
|
||||
$orig_update_trigger = $dbh->selectrow_arrayref("SELECT TRIGGER_SCHEMA,TRIGGER_NAME,DEFINER,ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS WHERE EVENT_MANIPULATION='UPDATE' AND ACTION_TIMING='AFTER' AND TRIGGER_SCHEMA='$orig_tbl->{db}' AND EVENT_OBJECT_TABLE='$orig_tbl->{tbl}';");
|
||||
}
|
||||
|
||||
if ( $o->get('preserve-triggers') && $orig_delete_trigger) {
|
||||
|
||||
# find if trigger has begin/end - supply insertion point
|
||||
my $delete_trigger_ins_point = trigger_ins_point(trigger => $orig_delete_trigger->[3]);
|
||||
|
||||
$delete_trigger
|
||||
= "CREATE TRIGGER `${prefix}_del` AFTER DELETE ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW";
|
||||
|
||||
my $delete_trigger_action
|
||||
= "DELETE IGNORE FROM $new_tbl->{name} "
|
||||
. "WHERE $del_index_cols";
|
||||
|
||||
if($delete_trigger_ins_point) {
|
||||
|
||||
$delete_trigger
|
||||
= "LOCK TABLES $orig_tbl->{name} WRITE;\n"
|
||||
. "DROP TRIGGER IF EXISTS `$orig_tbl->{db}`.`$orig_delete_trigger->[1]`;\n"
|
||||
. $delete_trigger . "\n"
|
||||
. terminate_sql(substr($orig_delete_trigger->[3], 0, $delete_trigger_ins_point))."\n"
|
||||
. $delete_trigger_action . ";\n"
|
||||
. "END;\n"
|
||||
. "UNLOCK TABLES;";
|
||||
}
|
||||
else {
|
||||
$delete_trigger
|
||||
= "LOCK TABLES $orig_tbl->{name} WRITE;\n"
|
||||
. "DROP TRIGGER IF EXISTS `$orig_tbl->{db}`.`$orig_delete_trigger->[1]`;\n"
|
||||
. $delete_trigger . "\n"
|
||||
. "BEGIN\n"
|
||||
. terminate_sql($orig_delete_trigger->[3]) . "\n"
|
||||
. $delete_trigger_action . ";\n"
|
||||
. "END;\n"
|
||||
. "UNLOCK TABLES;";
|
||||
}
|
||||
|
||||
my $definer = $orig_delete_trigger->[2];
|
||||
$definer =~ s/@/`@`/;
|
||||
$definer = "`".$definer."`";
|
||||
|
||||
$orig_triggers[0]
|
||||
= "CREATE DEFINER=$definer TRIGGER `$orig_delete_trigger->[0]`.`$orig_delete_trigger->[1]` "
|
||||
. "AFTER DELETE ON $new_tbl->{name}\n"
|
||||
. "FOR EACH ROW\n"
|
||||
. terminate_sql($orig_delete_trigger->[3]) . "\n";
|
||||
}
|
||||
else {
|
||||
$delete_trigger
|
||||
= "CREATE TRIGGER `${prefix}_del` AFTER DELETE ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW "
|
||||
. "DELETE IGNORE FROM $new_tbl->{name} "
|
||||
. "WHERE $del_index_cols";
|
||||
}
|
||||
|
||||
if ( $o->get('preserve-triggers') && $orig_insert_trigger) {
|
||||
|
||||
# find if trigger has begin/end - supply insertion point
|
||||
my $insert_trigger_ins_point = trigger_ins_point(trigger => $orig_insert_trigger->[3]);
|
||||
|
||||
$insert_trigger
|
||||
= "CREATE TRIGGER `${prefix}_ins` AFTER INSERT ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW";
|
||||
|
||||
my $insert_trigger_action
|
||||
= "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";
|
||||
|
||||
if($insert_trigger_ins_point) {
|
||||
$insert_trigger
|
||||
= "LOCK TABLES $orig_tbl->{name} WRITE;\n"
|
||||
. "DROP TRIGGER IF EXISTS `$orig_tbl->{db}`.`$orig_insert_trigger->[1]`;\n"
|
||||
. $insert_trigger . "\n"
|
||||
. terminate_sql(substr($orig_insert_trigger->[3], 0, $insert_trigger_ins_point)) . "\n"
|
||||
. $insert_trigger_action . ";\n"
|
||||
. "END;\n"
|
||||
. "UNLOCK TABLES;";
|
||||
}
|
||||
else {
|
||||
$insert_trigger
|
||||
= "LOCK TABLES $orig_tbl->{name} WRITE;\n"
|
||||
. "DROP TRIGGER IF EXISTS `$orig_tbl->{db}`.`$orig_insert_trigger->[1]`;\n"
|
||||
. $insert_trigger . "\n"
|
||||
. "BEGIN\n"
|
||||
. terminate_sql($orig_insert_trigger->[3]) . "\n"
|
||||
. $insert_trigger_action . ";\n"
|
||||
. "END; \n"
|
||||
. "UNLOCK TABLES;";
|
||||
}
|
||||
|
||||
my $definer = $orig_insert_trigger->[2];
|
||||
$definer =~ s/@/`@`/;
|
||||
$definer = "`".$definer."`";
|
||||
|
||||
$orig_triggers[1]
|
||||
= "CREATE DEFINER=$definer TRIGGER `$orig_insert_trigger->[0]`.`$orig_insert_trigger->[1]` "
|
||||
. "AFTER INSERT ON $new_tbl->{name}\n"
|
||||
. "FOR EACH ROW\n"
|
||||
. terminate_sql($orig_insert_trigger->[3]) . "\n";
|
||||
}
|
||||
else {
|
||||
$insert_trigger
|
||||
= "CREATE TRIGGER `${prefix}_ins` AFTER INSERT ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW "
|
||||
. "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ( $o->get('preserve-triggers') && $orig_update_trigger) {
|
||||
|
||||
# find if trigger has begin/end - supply insertion point
|
||||
my $update_trigger_ins_point = trigger_ins_point(trigger => $orig_update_trigger->[3]);
|
||||
|
||||
$update_trigger
|
||||
= "CREATE TRIGGER `${prefix}_upd` AFTER UPDATE ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW";
|
||||
|
||||
my $update_trigger_action
|
||||
= "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";
|
||||
|
||||
if($update_trigger_ins_point) {
|
||||
$update_trigger
|
||||
= "LOCK TABLES $orig_tbl->{name} WRITE;\n"
|
||||
. "DROP TRIGGER IF EXISTS `$orig_tbl->{db}`.`$orig_update_trigger->[1]`;\n"
|
||||
. $update_trigger . "\n"
|
||||
. terminate_sql(substr($orig_update_trigger->[3], 0, $update_trigger_ins_point)) . "\n"
|
||||
. $update_trigger_action . ";\n"
|
||||
. "END;\n"
|
||||
. "UNLOCK TABLES;";
|
||||
}
|
||||
else {
|
||||
$update_trigger
|
||||
= "LOCK TABLES $orig_tbl->{name} WRITE;\n"
|
||||
. "DROP TRIGGER IF EXISTS `$orig_tbl->{db}`.`$orig_update_trigger->[1]`;\n"
|
||||
. $update_trigger . "\n"
|
||||
. "BEGIN\n"
|
||||
. terminate_sql($orig_update_trigger->[3]) . "\n"
|
||||
. $update_trigger_action . ";\n"
|
||||
. "END;\n"
|
||||
. "UNLOCK TABLES;";
|
||||
}
|
||||
|
||||
my $definer = $orig_update_trigger->[2];
|
||||
$definer =~ s/@/`@`/;
|
||||
$definer = "`".$definer."`";
|
||||
|
||||
$orig_triggers[2]
|
||||
= "CREATE DEFINER=$definer TRIGGER `$orig_update_trigger->[0]`.`$orig_update_trigger->[1]` "
|
||||
. "AFTER UPDATE ON $new_tbl->{name}\n"
|
||||
. "FOR EACH ROW\n"
|
||||
. terminate_sql($orig_update_trigger->[3]) . "\n";
|
||||
}
|
||||
else {
|
||||
|
||||
$update_trigger
|
||||
= "CREATE TRIGGER `${prefix}_upd` AFTER UPDATE ON $orig_tbl->{name} "
|
||||
. "FOR EACH ROW "
|
||||
. "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";
|
||||
}
|
||||
|
||||
my @triggers = (
|
||||
['del', $delete_trigger],
|
||||
@@ -10899,6 +11080,40 @@ sub ts {
|
||||
return $msg ? "$ts $msg" : $ts;
|
||||
}
|
||||
|
||||
# find point in trigger we can insert pt-osc code for --preserve-triggers
|
||||
sub trigger_ins_point {
|
||||
my ( %args ) = @_;
|
||||
my @required_args = qw(trigger);
|
||||
foreach my $arg ( @required_args ) {
|
||||
die "I need a $arg argument" unless defined $args{$arg};
|
||||
}
|
||||
my ($trigger) = @args{@required_args};
|
||||
|
||||
my $ins_point;
|
||||
if ($trigger =~ /begin(.*?)end(?!.*end)/igms) {
|
||||
$ins_point = $+[0] - 3;
|
||||
}
|
||||
else { $ins_point = 0;}
|
||||
|
||||
return $ins_point;
|
||||
}
|
||||
|
||||
# sub to add ; if line doesn't end in ;
|
||||
sub terminate_sql {
|
||||
my ( $text ) = @_;
|
||||
die "I need a text argument" unless defined $text;
|
||||
$text = trim($text);
|
||||
if(substr($text, -1) ne ';') { $text .= ';'; }
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub trim {
|
||||
my ( $text ) = @_;
|
||||
die "I need a text argument" unless defined $text;
|
||||
$text =~ s/^\s+|\s+$//g;
|
||||
return $text;
|
||||
}
|
||||
|
||||
# Catches signals so we can exit gracefully.
|
||||
sub sig_int {
|
||||
my ( $signal ) = @_;
|
||||
@@ -11616,6 +11831,11 @@ until queries are running normally again. This will not prevent queueing,
|
||||
however; it will only give the server a chance to recover from the queueing. If
|
||||
you notice queueing, it is best to decrease the chunk time.
|
||||
|
||||
=item --preserve-triggers
|
||||
|
||||
Preserves old triggers when specified. This mode will briefly hold up to 3
|
||||
locks when it rewrites original triggers to include the tool code.
|
||||
|
||||
=item --new-table-name
|
||||
|
||||
type: string; default: %T_new
|
||||
|
Reference in New Issue
Block a user