package Db::MysqlShellBackend; use strict; use Db::ShellBackend; use AgentConfig; use Db::MysqlUtils; use vars qw|@ISA|; @ISA = qw|Db::ShellBackend|; # # 'name', 'user', 'password'[, 'host'][, 'socket'][, 'port'][, 'preload_dirs'][, 'utf8names'] # sub _init { my ($self, %params) = @_; $self->SUPER::_init(%params, 'type'=>'mysql'); if (defined $params{host} and defined $params{socket}) { return "socket & host are mutually exclusive"; } $self->{socket} = $params{socket} if defined $params{socket}; $self->{preload_dirs} = $params{preload_dirs} if defined $params{preload_dirs}; $self->{utf8names} = $params{utf8names} if defined $params{utf8names}; $self->{not_compatible_mysql_323} = $params{not_compatible_mysql_323} if defined $params{not_compatible_mysql_323}; $self->{tables} = join(' ', @{$params{tables}}) if defined $params{tables}; $self->{replace_and_no_create_info} = 1 if defined $params{replace_and_no_create_info}; } sub description { my ($self) = @_; return "mysql shell connection. " . $self->SUPER::description(); } sub _getCmd { my ($self, $cmd, $additionalOptions) = @_; my $addTables = $cmd eq AgentConfig::mysqldumpBin(); if ($self->{password}) { if ($cmd eq AgentConfig::mysqldumpBin()) { my $fileName = POSIX::tmpnam(); open OPTSFILE, ">$fileName"; close OPTSFILE; chmod 0600, $fileName; open OPTSFILE, ">$fileName"; print OPTSFILE "[mysqldump]\npassword=" . $self->{password}; close OPTSFILE; $cmd .= " --defaults-extra-file=" . $fileName; $self->_setFileToRemove($fileName); } else { $cmd .= " -p'$self->{password}'"; } } $cmd .= " --socket='$self->{socket}'" if $self->{socket}; $cmd .= " -h '$self->{host}'" if $self->{host}; $cmd .= " -u '$self->{user}'"; $cmd .= " -P '$self->{port}'" if $self->{port}; $cmd .= " $additionalOptions" if $additionalOptions; $cmd .= " '$self->{name}'"; $cmd .= " --tables $self->{tables} " if $self->{tables} and $addTables; $cmd .= " --no-create-info --insert-ignore" if $self->{replace_and_no_create_info} and $addTables; $cmd = "LD_PRELOAD=$self->{preload_dirs} $cmd" if $self->{preload_dirs}; return $cmd; } sub _mysql41OrNewer { my ($self) = @_; my $version = `$self->{cmdline} --version`; if ($version and $version =~ /Distrib (\d+)\.(\d+)\./) { return $1 > 4 || ($1 == 4 and $2 >= 1); } } sub connect { my ($self) = @_; if (!AgentConfig::mysqlBin()) { $self->_set_errstr("Unable to find 'mysql'"); return; } my $version = Db::MysqlUtils::getVersion(); $self->{cmdline} = $self->_getCmd(AgentConfig::mysqlBin(), "-B" . ($version && $version eq "3.23" ? "" : " -b")); if ($self->{utf8names} && Db::MysqlUtils::doesSupportCharacterSets($self)) { $self->{sql_prefix} = Db::MysqlUtils::getCharacterSetsSupportSql() . ";"; $self->{sql_prefix} .= Db::MysqlUtils::getNamesCharacterSetsSupportSql() . ";"; } if (!AgentConfig::mysqldumpBin()) { $self->_set_errstr("Unable to find 'mysqldump'"); return; } my $dumpoptions = "--quick --quote-names --add-drop-table"; if( $self->_mysql41OrNewer() ){ # $dumpoptions .= " --default-character-set=utf8 --set-charset" if $self->_mysql41OrNewer(); # $dumpoptions .= " --compatible=mysql323 " if !$self->{not_compatible_mysql_323} && $self->_mysql41OrNewer(); if ($self->{utf8names}) { $dumpoptions .= " --default-character-set=utf8 --set-charset"; }else{ my $charset = Db::MysqlUtils::getDatabaseCharacterSet( $self ); $dumpoptions .= " --default-character-set=$charset --set-charset" if $charset; } } my $dumpcmd = $self->_getCmd(AgentConfig::mysqldumpBin(), $dumpoptions); if (!$self->_mysql41OrNewer()) { $dumpcmd .= " | grep -v '^---'"; # http://bugs.mysql.com/bug.php?id=1056 } $self->_setDumpCmdline($dumpcmd); my $res = $self->execute("SHOW TABLES"); $self->finish(); return $res; } # # unquotes \n \t \\ in the string # used in parsing mysql batch output # sub _unquote { my ($str) = @_; my $len = length($str); my $res = ""; for (my $i = 0; $i <= $len; ++$i) { my $s = substr($str, $i, 1); if ($i != $len and $s eq "\\") { my $s = substr($str, ++$i, 1); $res .= "\n" if $s eq "n"; $res .= "\t" if $s eq "t"; $res .= "\\" if $s eq "\\"; } else { $res .= $s; } } return $res; } sub execute { my ($self, $sql) = @_; if (defined $self->{sql_prefix}) { $sql = $self->{sql_prefix} . $sql; } # quote ` from the shell and ' from the string $sql =~ s/'/'\\''/g; my @out = `echo '$sql' | $self->{cmdline}`; chomp @out; if ($#out > 0 and $out[0] =~ /^error/i) { $self->_set_errstr("Error: unable to execute $sql:\n" . (join "\n", @out)); return; } my @res = map {[map {$_ eq 'NULL' ? undef : _unquote($_)} split (/\t/, $_, -1)]} @out; $self->_set_execute_result(\@res); return 1; } 1;