# Copyright 1999-2012. Parallels IP Holdings GmbH. All Rights Reserved.
package Db::MysqlDbiBackend;

use strict;
use Db::DbiBackend;
use Db::MysqlUtils;

use vars qw|@ISA|;

@ISA = qw|Db::DbiBackend|;

#
# 'name', 'user', 'password'[, 'host'][, 'socket'][, 'port'][, '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->{port} = $params{port} if defined $params{port};
  $self->{utf8names} = $params{utf8names} if defined $params{utf8names};
  $self->{connRetries} = (defined $params{connRetries}) ? $params{connRetries} : 0;
}

sub _composeDbiAddress {
  my ($self) = @_;

  my $address = $self->SUPER::_composeDbiAddress();
  $address .= ";mysql_socket=$self->{socket}" if defined $self->{socket};
  $address .= ";port=$self->{port}" if defined $self->{port};
  return $address;
}

sub connect {
  my ($self) = @_;

  my $retry = 0;
  while (!$self->SUPER::connect()) {
    return if ($self->errcode() != 1040 # Too many connections
      || ++$retry > $self->{connRetries});
    sleep 1;
  }

  if ($self->{utf8names} && Db::MysqlUtils::doesSupportCharacterSets($self)) {
    $self->execute(Db::MysqlUtils::getCharacterSetsSupportSql());
    $self->execute(Db::MysqlUtils::getNamesCharacterSetsSupportSql());
  }

  return 1;
}

sub execute {
  my ($self, $sql, $quiet, @params) = @_;

  for (my $attempt = 0; $attempt < 2; $attempt++) {  # 2 attempts for case when connection to the server was lost
    # Prepare query
    $self->{result} = $self->{dbh}->prepare($sql);
    if (!$self->{result}) {
      Logging::warning("Unable to prepare SQL for execution: $DBI::errstr");
      $self->_set_errstr("Unable to prepare SQL ( for execution: $DBI::errstr");
      # simply return nothing in case of error and hope the agent won't crash with it
      return;
    }

    # Execute query
    my $res = $self->{result}->execute(@params);
    if (!defined $res && $DBI::err != 2006) {
      Logging::warning("Unable to execute SQL: $DBI::errstr. SQL query: $sql");
      $self->_set_errstr("Unable to execute SQL: $DBI::errstr");
      # simply return nothing in case of error and hope the agent won't crash with it
      return;
    }

    # Return result if query was executed successfully, otherwise:
    # - try to reconnect if that is the 1st attempt MySQL server has gone away
    # - return undef if that is the 2nd attempt
    if ($res) {
      return $res;
    } else {
      if ($attempt == 0) { # for the first attempt - try to reconnect
        # Reconnect on timeout
        Logging::debug("Timeout occurred during MySQL query. Reconnecting.");
        $self->{dbh}->disconnect();
        die if !$self->connect();
      } else { # for the second attempt - simply return nothing and hope the agent won't crash with it
        return;
      }
    }
  }
}

1;
