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);

  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};
}

sub description {
  my ($self) = @_;
  return "mysql shell connection. " . $self->SUPER::description();
}

sub _getCmd {
  my ($self, $cmd, $additionalOptions) = @_;

  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 = "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;
     }
  }

  $self->_setDumpCmdline($self->_getCmd(AgentConfig::mysqldumpBin(), $dumpoptions));

  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/, $_)]} @out;

  $self->_set_execute_result(\@res);
  return 1;
}

1;
