diff --git a/bin/pt-archiver b/bin/pt-archiver index 6e78dcc4..54bb72e4 100755 --- a/bin/pt-archiver +++ b/bin/pt-archiver @@ -2112,7 +2112,8 @@ sub get_cxn_params { . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" } grep { defined $info->{$_} } qw(F h P S A)) - . ';mysql_read_default_group=client'; + . ';mysql_read_default_group=client' + . ($info->{L} ? ';mysql_local_infile=1' : ''); } PTDEBUG && _d($dsn); return ($dsn, $info->{u}, $info->{p}); @@ -2141,6 +2142,9 @@ sub get_dbh { mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/i ? 1 : 0), }; @{$defaults}{ keys %$opts } = values %$opts; + if (delete $defaults->{L}) { # L for LOAD DATA LOCAL INFILE, our own extension + $defaults->{mysql_local_infile} = 1; + } if ( $opts->{mysql_use_result} ) { $defaults->{mysql_use_result} = 1; @@ -6433,6 +6437,10 @@ waits until the insertion is successful. The L<"--low-priority-insert">, L<"--replace">, and L<"--ignore"> options work with this option, but L<"--delayed-insert"> does not. +If C throws an error in the lines of C, refer to the documentation +for the C DSN option. + =item --charset short form: -A; type: string @@ -7163,6 +7171,26 @@ copy: yes Index to use. +=item * L + +copy: yes + +Explicitly enable LOAD DATA LOCAL INFILE. + +For some reason, some vendors compile libmysql without the +--enable-local-infile option, which disables the statement. This can +lead to weird situations, like the server allowing LOCAL INFILE, but +the client throwing exceptions if it's used. + +However, as long as the server allows LOAD DATA, clients can easily +reenable it; See L +and L. +This option does exactly that. + +Although we've not found a case where turning this option leads to errors or +differing behavior, to be on the safe side, this option is not +on by default. + =item * m copy: no diff --git a/bin/pt-upgrade b/bin/pt-upgrade index 157da959..396833aa 100755 --- a/bin/pt-upgrade +++ b/bin/pt-upgrade @@ -245,7 +245,8 @@ sub get_cxn_params { . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" } grep { defined $info->{$_} } qw(F h P S A)) - . ';mysql_read_default_group=client'; + . ';mysql_read_default_group=client' + . ($info->{L} ? ';mysql_local_infile=1' : ''); } PTDEBUG && _d($dsn); return ($dsn, $info->{u}, $info->{p}); @@ -274,6 +275,9 @@ sub get_dbh { mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/i ? 1 : 0), }; @{$defaults}{ keys %$opts } = values %$opts; + if (delete $defaults->{L}) { # L for LOAD DATA LOCAL INFILE, our own extension + $defaults->{mysql_local_infile} = 1; + } if ( $opts->{mysql_use_result} ) { $defaults->{mysql_use_result} = 1; @@ -12936,6 +12940,26 @@ dsn: host; copy: yes Connect to host. +=item * L + +copy: yes + +Explicitly enable LOAD DATA LOCAL INFILE. + +For some reason, some vendors compile libmysql without the +--enable-local-infile option, which disables the statement. This can +lead to weird situations, like the server allowing LOCAL INFILE, but +the client throwing exceptions if it's used. + +However, as long as the server allows LOAD DATA, clients can easily +reenable it; See L +and L. +This option does exactly that. + +Although we've not found a case where turning this option leads to errors or +differing behavior, to be on the safe side, this option is not +on by default. + =item * p dsn: password; copy: yes diff --git a/lib/DSNParser.pm b/lib/DSNParser.pm index 43625496..5e6dff1d 100644 --- a/lib/DSNParser.pm +++ b/lib/DSNParser.pm @@ -244,7 +244,8 @@ sub get_cxn_params { . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" } grep { defined $info->{$_} } qw(F h P S A)) - . ';mysql_read_default_group=client'; + . ';mysql_read_default_group=client' + . ($info->{L} ? ';mysql_local_infile=1' : ''); } PTDEBUG && _d($dsn); return ($dsn, $info->{u}, $info->{p}); @@ -277,6 +278,9 @@ sub get_dbh { mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/i ? 1 : 0), }; @{$defaults}{ keys %$opts } = values %$opts; + if (delete $defaults->{L}) { # L for LOAD DATA LOCAL INFILE, our own extension + $defaults->{mysql_local_infile} = 1; + } # Only add this if explicitly set because we're not sure if # mysql_use_result=0 would leave default mysql_store_result diff --git a/lib/PerconaTest.pm b/lib/PerconaTest.pm index 974da1a2..e06b7ee3 100644 --- a/lib/PerconaTest.pm +++ b/lib/PerconaTest.pm @@ -138,6 +138,12 @@ our $dsn_opts = [ dsn => 'user', copy => 1, }, + { + key => 'L', + desc => 'Pass mysql_local_infile to DBD::mysql', + dsn => 'mysql_local_infile', + copy => 1, + }, ]; # Runs code, captures and returns its output. @@ -788,7 +794,7 @@ sub tables_used { sub can_load_data { my $output = `/tmp/12345/use -e "SELECT * FROM percona_test.load_data" 2>/dev/null`; - return ($output || '') =~ /42/; + return ($output || '') =~ /1/; } 1; diff --git a/lib/Sandbox.pm b/lib/Sandbox.pm index 413db6b9..dd880925 100644 --- a/lib/Sandbox.pm +++ b/lib/Sandbox.pm @@ -116,6 +116,10 @@ sub get_dbh_for { my $dp = $self->{DSNParser}; my $dsn = $dp->parse('h=127.0.0.1,u=msandbox,p=msandbox,P=' . $port_for{$server}); my $dbh; + # This is primarily for the benefit of CompareResults, but it's + # also quite convenient when using an affected OS + $cxn_ops->{L} = 1 if !exists $cxn_ops->{L} + && !$self->can_load_data('master'); eval { $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), $cxn_ops) }; if ( $EVAL_ERROR ) { PTDEBUG && _d('Failed to get dbh for', $server, ':', $EVAL_ERROR); @@ -396,6 +400,7 @@ sub clear_genlogs { return; } + sub is_cluster_node { my ($self, $server) = @_; @@ -410,6 +415,12 @@ sub is_cluster_node { : 0; } +sub can_load_data { + my ($self, $server) = @_; + my $output = $self->use($server, q{-e "SELECT * FROM percona_test.load_data"}); + return ($output || '') =~ /1/; +} + 1; } # ########################################################################### diff --git a/sandbox/test-env b/sandbox/test-env index 4e0d5d27..d6df069c 100755 --- a/sandbox/test-env +++ b/sandbox/test-env @@ -318,7 +318,7 @@ case $opt in # LOAD DATA is disabled or broken on some boxes. # PerconaTest exports $can_load_data which is true - # if percona_test.load_data has the 42 row, + # if percona_test.load_data has a row with 1, # signaling that LOAD DATA LOCAL INFILE worked. ../util/check-load-data diff --git a/t/lib/CompareResults.t b/t/lib/CompareResults.t index db03f433..6a5db879 100644 --- a/t/lib/CompareResults.t +++ b/t/lib/CompareResults.t @@ -303,7 +303,6 @@ is_deeply( # ############################################################################# my $tmpdir = '/tmp/mk-upgrade-res'; SKIP: { - skip "LOAD DATA LOCAL INFILE is disabled", 30 unless $can_load_data; diag(`rm -rf $tmpdir 2>/dev/null; mkdir $tmpdir`); @@ -728,7 +727,6 @@ is_deeply( ); SKIP: { - skip "LOAD DATA LOCAL INFILE is disabled", 2 unless $can_load_data; $cr = new CompareResults( method => 'rows', diff --git a/t/lib/DSNParser.t b/t/lib/DSNParser.t index 179317a2..3e6fa406 100644 --- a/t/lib/DSNParser.t +++ b/t/lib/DSNParser.t @@ -218,7 +218,7 @@ is_deeply ( { S => 'bar', h => 'host' } )) ], [ - 'DBI:mysql:foo;host=me;mysql_socket=bar;mysql_read_default_group=client', + 'DBI:mysql:foo;host=me;mysql_socket=bar;mysql_read_default_group=client;mysql_local_infile=1', 'a', 'b', ], @@ -234,7 +234,7 @@ is_deeply ( { S => 'bar', h => 'host' } )) ], [ - 'DBI:mysql:foo;host=me;mysql_socket=bar;charset=foo;mysql_read_default_group=client', + 'DBI:mysql:foo;host=me;mysql_socket=bar;charset=foo;mysql_read_default_group=client;mysql_local_infile=1', 'a', 'b', ], @@ -568,6 +568,42 @@ like( qr/\QUnknown system variable 'time_zoen'/, "get_dbh dies with an unknown system variable" ); +$dp->prop('set-vars', undef); + +# ############################################################################# +# LOAD DATA LOCAL INFILE broken in some platforms +# https://bugs.launchpad.net/percona-toolkit/+bug/821715 +# ############################################################################# + +SKIP: { + skip "LOAD DATA LOCAL INFILE already works here", 1 if $can_load_data; + my $dbh = $dp->get_dbh( $dp->get_cxn_params( $dsn ) ); + + use File::Temp qw(tempfile); + + my ($fh, $filename) = tempfile( 'load_data_test.XXXXXXX', TMPDIR => 1 ); + print { $fh } "42\n"; + close $fh or die "Cannot close $filename: $!"; + + $dbh->do(q{DROP DATABASE IF EXISTS bug_821715}); + $dbh->do(q{CREATE DATABASE bug_821715}); + $dbh->do(q{CREATE TABLE IF NOT EXISTS bug_821715.load_data (i int)}); + + eval { + $dbh->do(qq{LOAD DATA LOCAL INFILE '$filename' INTO TABLE bug_821715.load_data}); + }; + + is( + $EVAL_ERROR, + '', + "Even though LOCAL INFILE is off by default, the dbhs returned by DSNParser can use it" + ); + + unlink $filename; + + $dbh->do(q{DROP DATABASE IF EXISTS bug_821715}); + $dbh->disconnect(); +} # ############################################################################# # Done. diff --git a/t/pt-archiver/basics.t b/t/pt-archiver/basics.t index 25656fd8..e7e61154 100644 --- a/t/pt-archiver/basics.t +++ b/t/pt-archiver/basics.t @@ -184,7 +184,6 @@ ok( # Bug 903387: pt-archiver doesn't honor b=1 flag to create SQL_LOG_BIN statement # ############################################################################# SKIP: { - skip('LOAD DATA LOCAL INFILE is disabled', 3) if !$can_load_data; $sb->load_file('master', "t/pt-archiver/samples/bulk_regular_insert.sql"); $sb->wait_for_slaves(); diff --git a/t/pt-archiver/bulk_insert.t b/t/pt-archiver/bulk_insert.t index 4727f7f7..00409975 100644 --- a/t/pt-archiver/bulk_insert.t +++ b/t/pt-archiver/bulk_insert.t @@ -22,9 +22,6 @@ my $dbh = $sb->get_dbh_for('master'); if ( !$dbh ) { plan skip_all => 'Cannot connect to sandbox master'; } -elsif ( !$can_load_data ) { - plan skip_all => 'LOAD DATA LOCAL INFILE is disabled'; -} my $output; my $rows; @@ -41,7 +38,7 @@ $dbh->do('INSERT INTO `test`.`table_5_copy` SELECT * FROM `test`.`table_5`'); $output = output( sub { pt_archiver::main(qw(--no-ascend --limit 50 --bulk-insert), qw(--bulk-delete --where 1=1 --statistics), - '--source', "D=test,t=table_5,F=$cnf", + '--source', "L=1,D=test,t=table_5,F=$cnf", '--dest', "t=table_5_dest") }, ); like($output, qr/SELECT 105/, 'Fetched 105 rows'); @@ -66,7 +63,7 @@ $output = output( sub { pt_archiver::main( '--where', "id < 8", qw(--limit 100000 --txn-size 1000), qw(--why-quit --statistics --bulk-insert), - '--source', "D=bri,t=t,F=$cnf", + '--source', "L=1,D=bri,t=t,F=$cnf", '--dest', "t=t_arch") }, ); $rows = $dbh->selectall_arrayref('select id from bri.t order by id'); diff --git a/t/pt-archiver/bulk_regular_insert.t b/t/pt-archiver/bulk_regular_insert.t index 13879833..6c1e6705 100644 --- a/t/pt-archiver/bulk_regular_insert.t +++ b/t/pt-archiver/bulk_regular_insert.t @@ -22,9 +22,6 @@ my $dbh = $sb->get_dbh_for('master'); if ( !$dbh ) { plan skip_all => 'Cannot connect to sandbox master'; } -elsif ( !$can_load_data ) { - plan skip_all => 'LOAD DATA LOCAL INFILE is disabled'; -} my $output; my $cnf = "/tmp/12345/my.sandbox.cnf"; @@ -39,7 +36,7 @@ $sb->load_file('master', "t/pt-archiver/samples/bulk_regular_insert.sql"); $dbh->do('use bri'); output( - sub { pt_archiver::main("--source", "F=$cnf,D=bri,t=t", qw(--dest t=t_arch --where 1=1 --bulk-insert --limit 3)) }, + sub { pt_archiver::main("--source", "F=$cnf,D=bri,t=t,L=1", qw(--dest t=t_arch --where 1=1 --bulk-insert --limit 3)) }, ); my $t_rows = $dbh->selectall_arrayref('select * from t order by id'); @@ -73,7 +70,7 @@ is_deeply( $sb->load_file('master', "t/pt-archiver/samples/bulk_regular_insert.sql"); $dbh->do('use bri'); -`$cmd --source F=$cnf,D=bri,t=t --dest t=t_arch,m=bulk_regular_insert --where "1=1" --bulk-insert --limit 3`; +`$cmd --source F=$cnf,D=bri,t=t,L=1 --dest t=t_arch,m=bulk_regular_insert --where "1=1" --bulk-insert --limit 3`; my $bri_t_rows = $dbh->selectall_arrayref('select * from t order by id'); my $bri_t_arch_rows = $dbh->selectall_arrayref('select * from t_arch order by id'); diff --git a/t/pt-upgrade/basics.t b/t/pt-upgrade/basics.t index 04fcfc81..661be26d 100644 --- a/t/pt-upgrade/basics.t +++ b/t/pt-upgrade/basics.t @@ -30,7 +30,7 @@ elsif ( !$dbh2 ) { plan skip_all => 'Cannot connect to second sandbox master'; } -my @host_args = ('h=127.1,P=12345', 'P=12348'); +my @host_args = ('h=127.1,P=12345,L=1', 'P=12348'); my @op_args = (qw(-u msandbox -p msandbox), '--compare', 'results,warnings', '--zero-query-times', @@ -61,9 +61,6 @@ ok( 'Report for multiple queries (checksum method)' ); -SKIP: { - skip "LOAD DATA LOCAL INFILE is disabled", 2 unless $can_load_data; - ok( no_diff( sub { pt_upgrade::main(@args, "$trunk/$sample/001/select-one.log", @@ -81,7 +78,6 @@ SKIP: { ), 'Report for multiple queries (rows method)' ); -} ok( no_diff( @@ -108,9 +104,6 @@ $sb->wipe_clean($dbh2); # Issue 951: mk-upgrade "I need a db argument" error with # compare-results-method=rows # ############################################################################# -SKIP: { - skip "LOAD DATA LOCAL INFILE is disabled", 4 unless $can_load_data; - $sb->load_file('master', "$sample/002/tables.sql"); $sb->load_file('master1', "$sample/002/tables.sql"); @@ -120,7 +113,7 @@ SKIP: { ok( no_diff( sub { pt_upgrade::main(@op_args, "$log/002/no-db.log", - 'h=127.1,P=12345,D=test', 'P=12348,D=test', + 'h=127.1,P=12345,D=test,L=1', 'P=12348,D=test', qw(--compare-results-method rows --temp-database test)) }, "$sample/002/report-01.txt", ), @@ -134,7 +127,7 @@ SKIP: { ok( no_diff( sub { pt_upgrade::main(@op_args, "$log/002/no-db.log", - 'h=127.1,P=12345,D=test', 'P=12348,D=test', + 'h=127.1,P=12345,D=test,L=1', 'P=12348,D=test', qw(--compare-results-method rows --temp-database tmp_db)) }, "$sample/002/report-01.txt", ), @@ -155,15 +148,11 @@ SKIP: { $sb->wipe_clean($dbh1); $sb->wipe_clean($dbh2); -} # ############################################################################# # Bug 926598: DBD::mysql bug causes pt-upgrade to use wrong # precision (M) and scale (D) # ############################################################################# -SKIP: { - skip "LOAD DATA LOCAL INFILE is disabled", 2 unless $can_load_data; - $sb->load_file('master', "$sample/003/tables.sql"); $sb->load_file('master1', "$sample/003/tables.sql"); @@ -185,7 +174,6 @@ SKIP: { qr/[`"]SUM\(total\)[`"]\s+double\sDEFAULT/i, "No M,D in table def (bug 926598)" ); -} # ############################################################################# # SELECT FUNC(), so there are no tables. diff --git a/t/pt-upgrade/warnings.t b/t/pt-upgrade/warnings.t index d745a63e..c699f205 100644 --- a/t/pt-upgrade/warnings.t +++ b/t/pt-upgrade/warnings.t @@ -15,13 +15,6 @@ use PerconaTest; use Sandbox; require "$trunk/bin/pt-upgrade"; -# This test calls pt-upgrade with --compare-results-method rows -# which use LOAD DATA LOCAL INFILE. If LOAD DATA is disabled, -# then this this test can't run. -if ( !$can_load_data ) { - plan skip_all => 'LOAD DATA LOCAL INFILE is disabled'; -} - # This runs immediately if the server is already running, else it starts it. diag(`$trunk/sandbox/start-sandbox master 12348 >/dev/null`); @@ -43,7 +36,7 @@ $sb->load_file('master', 't/pt-upgrade/samples/001/tables.sql'); $sb->load_file('master1', 't/pt-upgrade/samples/001/tables.sql'); my $output; -my $cmd = "$trunk/bin/pt-upgrade h=127.1,P=12345,u=msandbox,p=msandbox P=12348 --compare results,warnings --zero-query-times --compare-results-method rows --limit 10"; +my $cmd = "$trunk/bin/pt-upgrade h=127.1,P=12345,u=msandbox,p=msandbox,L=1 P=12348 --compare results,warnings --zero-query-times --compare-results-method rows --limit 10"; # This test really deals with, # http://code.google.com/p/maatkit/issues/detail?id=754 diff --git a/util/check-load-data b/util/check-load-data index e6b7d9e4..69f25577 100755 --- a/util/check-load-data +++ b/util/check-load-data @@ -41,17 +41,21 @@ my $dbh = DBI->connect( $dbh->do("CREATE TABLE IF NOT EXISTS percona_test.load_data (i int)"); -`echo 42 > /tmp/load_data_test.$$`; +`echo 1 > /tmp/load_data_test.$$`; eval { $dbh->do("LOAD DATA LOCAL INFILE '/tmp/load_data_test.$$' INTO TABLE percona_test.load_data"); }; +if ( $EVAL_ERROR ) { + $dbh->do("INSERT INTO percona_test.load_data (i) VALUES (0)"); +} + unlink "/tmp/load_data_test.$$"; my ($val) = $dbh->selectrow_array("SELECT i FROM percona_test.load_data"); -if ( ($val || 0) == 42 ) { +if ( ($val || 0) == 1 ) { print "LOAD DATA LOCAL INFILE is enabled\n"; } else {