Files
percona-toolkit/t/lib/DSNParser.t

469 lines
11 KiB
Perl

#!/usr/bin/perl
BEGIN {
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
};
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More tests => 27;
use DSNParser;
use OptionParser;
use PerconaTest;
my $opts = [
{
key => 'A',
desc => 'Default character set',
dsn => 'charset',
copy => 1,
},
{
key => 'D',
desc => 'Database to use',
dsn => 'database',
copy => 1,
},
{
key => 'F',
desc => 'Only read default options from the given file',
dsn => 'mysql_read_default_file',
copy => 1,
},
{
key => 'h',
desc => 'Connect to host',
dsn => 'host',
copy => 1,
},
{
key => 'p',
desc => 'Password to use when connecting',
dsn => 'password',
copy => 1,
},
{
key => 'P',
desc => 'Port number to use for connection',
dsn => 'port',
copy => 1,
},
{
key => 'S',
desc => 'Socket file to use for connection',
dsn => 'mysql_socket',
copy => 1,
},
{
key => 'u',
desc => 'User for login if not current user',
dsn => 'user',
copy => 1,
},
];
my $dp = new DSNParser(opts => $opts);
is_deeply(
$dp->parse('u=a,p=b'),
{ u => 'a',
p => 'b',
S => undef,
h => undef,
P => undef,
F => undef,
D => undef,
A => undef,
},
'Basic DSN'
);
is_deeply(
$dp->parse('u=a,p=b,A=utf8'),
{ u => 'a',
p => 'b',
S => undef,
h => undef,
P => undef,
F => undef,
D => undef,
A => 'utf8',
},
'Basic DSN with charset'
);
# The test that was here is no longer needed now because
# all opts must be specified now.
is_deeply(
$dp->parse('u=a,p=b', { D => 'foo', h => 'me' }, { S => 'bar', h => 'host' } ),
{ D => 'foo',
F => undef,
h => 'me',
p => 'b',
P => undef,
S => 'bar',
u => 'a',
A => undef,
},
'DSN with defaults'
);
is(
$dp->as_string(
$dp->parse('u=a,p=b', { D => 'foo', h => 'me' }, { S => 'bar', h => 'host' } )
),
'D=foo,S=bar,h=me,p=...,u=a',
'DSN stringified when it gets DSN as arg'
);
is(
$dp->as_string(
'D=foo,S=bar,h=me,p=b,u=a',
),
'D=foo,S=bar,h=me,p=b,u=a',
'DSN stringified when it gets a string as arg'
);
is (
$dp->as_string({ bez => 'bat', h => 'foo' }),
'h=foo',
'DSN stringifies without extra crap',
);
is (
$dp->as_string({ h=>'localhost', P=>'3306',p=>'omg'}, [qw(h P)]),
'h=localhost,P=3306',
'DSN stringifies only requested parts'
);
# The test that was here is no longer need due to issue 55.
# DSN usage comes from the POD now.
$dp->prop('autokey', 'h');
is_deeply(
$dp->parse('automatic'),
{ D => undef,
F => undef,
h => 'automatic',
p => undef,
P => undef,
S => undef,
u => undef,
A => undef,
},
'DSN with autokey'
);
$dp->prop('autokey', 'h');
is_deeply(
$dp->parse('localhost,A=utf8'),
{ u => undef,
p => undef,
S => undef,
h => 'localhost',
P => undef,
F => undef,
D => undef,
A => 'utf8',
},
'DSN with an explicit key and an autokey',
);
is_deeply(
$dp->parse('automatic',
{ D => 'foo', h => 'me', p => 'b' },
{ S => 'bar', h => 'host', u => 'a' } ),
{ D => 'foo',
F => undef,
h => 'automatic',
p => 'b',
P => undef,
S => 'bar',
u => 'a',
A => undef,
},
'DSN with defaults and an autokey'
);
# The test that was here is no longer need due to issue 55.
# DSN usage comes from the POD now.
is_deeply (
[
$dp->get_cxn_params(
$dp->parse(
'u=a,p=b',
{ D => 'foo', h => 'me' },
{ S => 'bar', h => 'host' } ))
],
[
'DBI:mysql:foo;host=me;mysql_socket=bar;mysql_read_default_group=client',
'a',
'b',
],
'Got connection arguments',
);
is_deeply (
[
$dp->get_cxn_params(
$dp->parse(
'u=a,p=b,A=foo',
{ D => 'foo', h => 'me' },
{ S => 'bar', h => 'host' } ))
],
[
'DBI:mysql:foo;host=me;mysql_socket=bar;charset=foo;mysql_read_default_group=client',
'a',
'b',
],
'Got connection arguments with charset',
);
# Make sure we can connect to MySQL with a charset
my $d = $dp->parse('h=127.0.0.1,P=12345,A=utf8,u=msandbox,p=msandbox');
my $dbh;
eval {
$dbh = $dp->get_dbh($dp->get_cxn_params($d), {});
};
SKIP: {
skip 'Cannot connect to sandbox master', 6 if $EVAL_ERROR;
$dp->fill_in_dsn($dbh, $d);
is($d->{P}, 12345, 'Left port alone');
is($d->{u}, 'msandbox', 'Filled in username');
is($d->{S}, '/tmp/12345/mysql_sandbox12345.sock', 'Filled in socket');
is($d->{h}, '127.0.0.1', 'Left hostname alone');
is_deeply(
$dbh->selectrow_arrayref('select @@character_set_client, @@character_set_connection, @@character_set_results'),
[qw(utf8 utf8 utf8)],
'Set charset'
);
$dbh->disconnect();
# Issue 1282: Enabling utf8 with --charset (-A) is case-sensitive
# This test really doesn't do anything because the problem is in the line,
# mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0),
# in get_dbh(). That line is part of a hashref declaration so we
# have no access to it here. I keep this this test because it allows
# me to look manually via PTDEBUG and see that mysql_enable_utf8=>1
# even if A=UTF8.
$d = $dp->parse('h=127.0.0.1,P=12345,A=UTF8,u=msandbox,p=msandbox');
eval {
$dbh = $dp->get_dbh($dp->get_cxn_params($d), {});
};
is_deeply(
$dbh->selectrow_arrayref('select @@character_set_client, @@character_set_connection, @@character_set_results'),
[qw(utf8 utf8 utf8)],
'Set utf8 charset case-insensitively (issue 1282)'
);
};
$dp->prop('dbidriver', 'Pg');
is_deeply (
[
$dp->get_cxn_params(
{
u => 'a',
p => 'b',
h => 'me',
D => 'foo',
},
)
],
[
'DBI:Pg:dbname=foo;host=me',
'a',
'b',
],
'Got connection arguments for PostgreSQL',
);
$dp->prop('required', { h => 1 } );
throws_ok (
sub { $dp->parse('u=b') },
qr/Missing required DSN option 'h' in 'u=b'/,
'Missing host part',
);
throws_ok (
sub { $dp->parse('h=foo,Z=moo') },
qr/Unknown DSN option 'Z' in 'h=foo,Z=moo'/,
'Extra key',
);
# #############################################################################
# Test parse_options().
# #############################################################################
my $o = new OptionParser(
description => 'parses command line options.',
dp => $dp,
);
$o->_parse_specs(
{ spec => 'defaults-file|F=s', desc => 'defaults file' },
{ spec => 'password|p=s', desc => 'password' },
{ spec => 'host|h=s', desc => 'host' },
{ spec => 'port|P=i', desc => 'port' },
{ spec => 'socket|S=s', desc => 'socket' },
{ spec => 'user|u=s', desc => 'user' },
);
@ARGV = qw(--host slave1 --user foo);
$o->get_opts();
is_deeply(
$dp->parse_options($o),
{
D => undef,
F => undef,
h => 'slave1',
p => undef,
P => undef,
S => undef,
u => 'foo',
A => undef,
},
'Parses DSN from OptionParser obj'
);
# #############################################################################
# Test copy().
# #############################################################################
push @$opts, { key => 't', desc => 'table' };
$dp = new DSNParser(opts => $opts);
my $dsn_1 = {
D => undef,
F => undef,
h => 'slave1',
p => 'p1',
P => '12345',
S => undef,
t => undef,
u => 'foo',
A => undef,
};
my $dsn_2 = {
D => 'test',
F => undef,
h => undef,
p => 'p2',
P => undef,
S => undef,
t => 'tbl',
u => undef,
A => undef,
};
is_deeply(
$dp->copy($dsn_1, $dsn_2),
{
D => 'test',
F => undef,
h => 'slave1',
p => 'p2',
P => '12345',
S => undef,
t => 'tbl',
u => 'foo',
A => undef,
},
'Copy DSN without overwriting destination'
);
is_deeply(
$dp->copy($dsn_1, $dsn_2, overwrite=>1),
{
D => 'test',
F => undef,
h => 'slave1',
p => 'p1',
P => '12345',
S => undef,
t => 'tbl',
u => 'foo',
A => undef,
},
'Copy DSN and overwrite destination'
);
# #############################################################################
# Issue 93: DBI error messages can include full SQL
# #############################################################################
SKIP: {
skip 'ShowErrorStatement requires DBD::mysql 4.003', 1 unless $DBD::mysql::VERSION ge '4.003';
skip 'Cannot connect to sandbox master', 1 unless $dbh;
eval { $dbh->do('SELECT * FROM doesnt.exist WHERE foo = 1'); };
like(
$EVAL_ERROR,
qr/SELECT \* FROM doesnt.exist WHERE foo = 1/,
'Includes SQL in error message (issue 93)'
);
};
# #############################################################################
# Issue 597: mk-slave-prefetch ignores --set-vars
# #############################################################################
# This affects all scripts because prop() doesn't match what get_dbh() does.
SKIP: {
skip 'Cannot connect to sandbox master', 1 unless $dbh;
$dbh->do('SET @@global.wait_timeout=1');
# This dbh is going to timeout too during this test so close
# it now else we'll get an error.
$dbh->disconnect();
$dp = new DSNParser(opts => $opts);
$dp->prop('set-vars', 'wait_timeout=1000');
$d = $dp->parse('h=127.0.0.1,P=12345,A=utf8,u=msandbox,p=msandbox');
my $dbh2 = $dp->get_dbh($dp->get_cxn_params($d), {mysql_use_result=>1});
sleep 2;
eval {
$dbh2->do('SELECT DATABASE()');
};
is(
$EVAL_ERROR,
'',
'SET vars (issue 597)'
);
$dbh2->disconnect();
# Have to reconnect $dbh since it timedout too.
$dbh = $dp->get_dbh($dp->get_cxn_params($d), {});
$dbh->do('SET @@global.wait_timeout=28800');
};
# #############################################################################
# Issue 801: DSNParser clobbers SQL_MODE
# #############################################################################
SKIP: {
diag(`SQL_MODE="no_zero_date" $trunk/sandbox/start-sandbox master 12348 >/dev/null`);
my $dsn = $dp->parse('h=127.1,P=12348,u=msandbox,p=msandbox');
my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {});
skip 'Cannot connect to second sandbox master', 1 unless $dbh;
my $row = $dbh->selectrow_arrayref('select @@sql_mode');
is(
$row->[0],
'NO_AUTO_VALUE_ON_ZERO,NO_ZERO_DATE',
"Did not clobber server SQL mode"
);
$dbh->disconnect();
diag(`$trunk/sandbox/stop-sandbox 12348 >/dev/null`);
};
# #############################################################################
# Done.
# #############################################################################
$dbh->disconnect() if $dbh;
exit;