diff --git a/lib/CopyRowsInsertSelect.pm b/lib/CopyRowsInsertSelect.pm index 7e6b2c1f..b210d902 100644 --- a/lib/CopyRowsInsertSelect.pm +++ b/lib/CopyRowsInsertSelect.pm @@ -37,13 +37,14 @@ use constant PTDEBUG => $ENV{PTDEBUG} || 0; # CopyRowsInsertSelect object sub new { my ( $class, %args ) = @_; - my @required_args = qw(Retry); + my @required_args = qw(Retry Quoter); foreach my $arg ( @required_args ) { die "I need a $arg argument" unless $args{$arg}; } my $self = { - %args, + Retry => $args{Retry}, + Quoter => $args{Quoter}, }; return bless $self, $class; @@ -56,9 +57,10 @@ sub copy { die "I need a $arg argument" unless $args{$arg}; } my ($dbh, $msg, $from_table, $to_table, $chunks) = @args{@required_args}; + my $q = $self->{Quoter}; my $pr = $args{Progress}; my $sleep = $args{sleep}; - my $columns = join(', ', @{$args{columns}}); + my $columns = join(', ', map { $q->quote($_) } @{$args{columns}}); my $n_chunks = @$chunks - 1; for my $chunkno ( 0..$n_chunks ) { diff --git a/t/lib/CopyRowsInsertSelect.t b/t/lib/CopyRowsInsertSelect.t index b5a4e0a1..361775df 100644 --- a/t/lib/CopyRowsInsertSelect.t +++ b/t/lib/CopyRowsInsertSelect.t @@ -17,6 +17,7 @@ use PerconaTest; use Progress; use Transformers; use Retry; +use Quoter; use CopyRowsInsertSelect; Transformers->import(qw(secs_to_time)); @@ -38,64 +39,124 @@ elsif ( !@{$dbh->selectcol_arrayref('SHOW DATABASES LIKE "sakila"')} ) { plan skip_all => "Sandbox master does not have the sakila database"; } else { - plan tests => 8; + plan tests => 14; } +my $q = new Quoter(); my $rr = new Retry(); -my $osc = new CopyRowsInsertSelect(Retry => $rr); +my $osc = new CopyRowsInsertSelect(Retry => $rr, Quoter => $q); my $msg = sub { print "$_[0]\n"; }; my $output = ""; +my $rows; -$sb->load_file("master", "t/lib/samples/osc/tbl001.sql"); -$dbh->do("USE osc"); +# ########################################################################### +# Copy simple tables. +# ########################################################################### -$osc->copy( - dbh => $dbh, - from_table => 'osc.t', - to_table => 'osc.__new_t', - columns => [qw(id c)], - chunks => ['1=1'], - msg => $msg, +sub test_copy_table { + my (%args) = @_; + my ($tbl, $col, $expect) = @args{qw(tbl col expect)}; + + $sb->load_file("master", "t/lib/samples/osc/$tbl"); + PerconaTest::wait_for_table($dbh, "osc.t", "id=5"); + $dbh->do("USE osc"); + + $osc->copy( + dbh => $dbh, + from_table => 'osc.t', + to_table => 'osc.__new_t', + columns => ['id', $col], + chunks => ['1=1'], + msg => $msg, + ); + + $rows = $dbh->selectall_arrayref("select id, `$col` from __new_t order by id"); + is_deeply( + $rows, + [ [1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e'], ], + "$tbl: One chunk copy" + ) or print Dumper($rows); + + $dbh->do("truncate table osc.__new_t"); + + ok( + no_diff( + sub { + $osc->copy( + dbh => $dbh, + from_table => 'osc.t', + to_table => 'osc.__new_t', + columns => ['id', $col], + chunks => ['id < 4', 'id >= 4 AND id < 6'], + msg => $msg, + print => 1, + engine_flags => 'LOCK IN SHARE MODE', + ); + }, + "t/lib/samples/osc/$expect", + stderr => 1, + ), + "$tbl: 2 chunk copy" + ); + + $rows = $dbh->selectall_arrayref("select id, `$col` from __new_t order by id"); + is_deeply( + $rows, + [], + "$tbl: print doesn't exec statements" + ); +} + +test_copy_table( + tbl => "tbl001.sql", + col => "c", + expect => "copyins001.txt", ); -my $rows = $dbh->selectall_arrayref("select id, c from __new_t order by id"); -is_deeply( - $rows, - [ [1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e'], ], - "One chunk copy" -) or print Dumper($rows); - - +# Sleep callback. +my $sleep_cnt = 0; $dbh->do("truncate table osc.__new_t"); -$output = output( sub { +output( sub { $osc->copy( - dbh => $dbh, - from_table => 'osc.t', - to_table => 'osc.__new_t', - columns => [qw(id c)], - chunks => ['id < 4', 'id >= 4 AND id < 6'], - msg => $msg, - print => 1, - engine_flags => 'LOCK IN SHARE MODE', + dbh => $dbh, + from_table => 'osc.t', + to_table => 'osc.__new_t', + columns => [qw(id c)], + chunks => ['id < 4', 'id >= 4 AND id < 6'], + msg => $msg, + sleep => sub { $sleep_cnt++; }, ); }); +is( + $sleep_cnt, + 1, + "Calls sleep callback after each chunk (except last chunk)" +); + +eval { + $output = output(sub { $osc->cleanup(); } ); +}; ok( - no_diff( - $output, - "t/lib/samples/osc/copyins001.txt", - cmd_output => 1, - ), - "Prints 2 SQL statments for the 2 chunks" + !$EVAL_ERROR && !$output, + "cleanup() works but doesn't do anything" ); -$rows = $dbh->selectall_arrayref("select id, c from __new_t order by id"); -is_deeply( - $rows, - [], - "Doesn't exec those statements if print is true" +test_copy_table( + tbl => "tbl002.sql", + col => "default", + expect => "copyins002.txt", ); +test_copy_table( + tbl => "tbl003.sql", + col => "space col", + expect => "copyins003.txt", +); + +# ########################################################################### +# Copy a larger, more complex sakila table. +# ########################################################################### $dbh->do('create table osc.city like sakila.city'); $dbh->do('alter table osc.city engine=myisam'); my $chunks = [ @@ -140,35 +201,6 @@ like( "Reports copy progress if Progress obj given" ); - -my $sleep_cnt = 0; - -$dbh->do("truncate table osc.__new_t"); -output( sub { - $osc->copy( - dbh => $dbh, - from_table => 'osc.t', - to_table => 'osc.__new_t', - columns => [qw(id c)], - chunks => ['id < 4', 'id >= 4 AND id < 6'], - msg => $msg, - sleep => sub { $sleep_cnt++; }, - ); -}); -is( - $sleep_cnt, - 1, - "Calls sleep callback after each chunk (except last chunk)" -); - -eval { - $output = output(sub { $osc->cleanup(); } ); -}; -ok( - !$EVAL_ERROR && !$output, - "cleanup() works but doesn't do anything" -); - # ############################################################################# # Done. # ############################################################################# diff --git a/t/lib/samples/osc/copyins001.txt b/t/lib/samples/osc/copyins001.txt index def1dd77..07200a9a 100644 --- a/t/lib/samples/osc/copyins001.txt +++ b/t/lib/samples/osc/copyins001.txt @@ -1,2 +1,2 @@ -INSERT IGNORE INTO osc.__new_t (id, c) SELECT id, c FROM osc.t WHERE (id < 4) LOCK IN SHARE MODE -INSERT IGNORE INTO osc.__new_t (id, c) SELECT id, c FROM osc.t WHERE (id >= 4 AND id < 6) LOCK IN SHARE MODE +INSERT IGNORE INTO osc.__new_t (`id`, `c`) SELECT `id`, `c` FROM osc.t WHERE (id < 4) LOCK IN SHARE MODE +INSERT IGNORE INTO osc.__new_t (`id`, `c`) SELECT `id`, `c` FROM osc.t WHERE (id >= 4 AND id < 6) LOCK IN SHARE MODE diff --git a/t/lib/samples/osc/copyins002.txt b/t/lib/samples/osc/copyins002.txt new file mode 100644 index 00000000..f20bb523 --- /dev/null +++ b/t/lib/samples/osc/copyins002.txt @@ -0,0 +1,2 @@ +INSERT IGNORE INTO osc.__new_t (`id`, `default`) SELECT `id`, `default` FROM osc.t WHERE (id < 4) LOCK IN SHARE MODE +INSERT IGNORE INTO osc.__new_t (`id`, `default`) SELECT `id`, `default` FROM osc.t WHERE (id >= 4 AND id < 6) LOCK IN SHARE MODE diff --git a/t/lib/samples/osc/copyins003.txt b/t/lib/samples/osc/copyins003.txt new file mode 100644 index 00000000..1ad162d3 --- /dev/null +++ b/t/lib/samples/osc/copyins003.txt @@ -0,0 +1,2 @@ +INSERT IGNORE INTO osc.__new_t (`id`, `space col`) SELECT `id`, `space col` FROM osc.t WHERE (id < 4) LOCK IN SHARE MODE +INSERT IGNORE INTO osc.__new_t (`id`, `space col`) SELECT `id`, `space col` FROM osc.t WHERE (id >= 4 AND id < 6) LOCK IN SHARE MODE