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

use strict;

use Logging;
use Db::MysqlDbiBackend;
use Db::MysqlShellBackend;
use Db::DbiBackend;
use Db::PostgresqlDbiBackend;
use Db::PostgresqlShellBackend;
use StopWatch;

use vars qw|$UsePostgreShellBackendAsSudo $backend|;

$UsePostgreShellBackendAsSudo = 0; #Set this if you want use Postgre shell backend runnig by sudo

#
# Required parameters: 'type', 'name', 'user', 'password'.
#
# 'type' is the 'mysql' or 'postgresql'.
#
# Optional parameters:
#   'host' - database host name, if remote.
#   'nodbi' - disable DBI modules probe and use.
#
# mysql:
#
# Optional parameters:
#   'socket' - mysql socket to connect to DB. Disables the DBI modules use.
#   'preload_dirs' - directories to add to LD_PRELOAD_PATH to connect to DB.
#      Also disables the DBI modules use.
#
# postgresql:
#
# No additional parameters.
#


sub getConnection {
  my $be = getBackend(@_);
  if ($be) {
    Logging::debug("New connection: " . $be->description());
    return Db::Connection->new($be);
  }
}

sub _getDbiBackend {
  my ($type, %params) = @_;

  if ($type eq 'mysql') {
    return Db::MysqlDbiBackend->new(%params);
  }

  if ($type eq 'postgresql') {
    return Db::PostgresqlDbiBackend->new(%params);
  }

  die "Unknown DBMS type: $type";
}

sub _getShellBackend {
  my ($type, %params) = @_;

  if ($type eq 'mysql') {
    return Db::MysqlShellBackend->new(%params);
  }

  if ($type eq 'postgresql') {
    return Db::PostgresqlShellBackend->new(%params);
  }

  die "Unknown DBMS type: $type";
}

sub getBackend {
  my (%params) = @_;

  my $type = $params{type};
  delete $params{type};

  my $dbi = 1;
  if( $type eq 'postgresql' and $UsePostgreShellBackendAsSudo ){
    $dbi = 0;
    Db::PostgresqlShellBackend::set_sudo( 1 );
  }
  if (defined $params{nodbi}) {
    $dbi = 0;
    delete $params{nodbi};
  }

  if ($dbi and !defined $params{preload_dirs} and !defined $params{socket}) {
    my $conn = _getDbiBackend($type, %params);
    return $conn if $conn->connect();

    if (!defined $params{host}) {
      my $conn = _getDbiBackend($type, %params, 'host' => 'localhost');
      return $conn if $conn->connect();
    }
  }

  delete $params{nodbi};

  my $conn = _getShellBackend($type, %params);
  return $conn if $conn->connect();

  if (!defined $params{host}) {
    my $conn = _getShellBackend($type, %params, 'host' => 'localhost');
    return $conn if $conn->connect();
  }
}

sub getDumpCmdLine {
  $backend = getBackend(@_, 'nodbi' => 1);
  return $backend->dumpCmdline();
}

sub getFileToRemove {
  if (ref($backend) =~ /Db::MysqlShellBackend/) {
    return $backend->getFileToRemove();
  }
}

sub isLogAllowed {
  if (ref($backend) =~ /Db::PostgresqlShellBackend/) {
    return 0;
  }
  return 1;
}

#
# Connection itself
#

sub new {
  my $self = {};
  bless($self, shift);
  $self->_init(@_);
  return $self;
}

sub _init {
  my ($self, $backend) = @_;
  $self->{backend} = $backend;
}

sub startCollectStatistics ( $ ) {
  my $self = shift(@_);

  $self->{collectStatistics} = 1;
  $self->{statistics}->{sqlTime} = 0;
  $self->{stopWatch} = StopWatch->new();
}

sub stopCollectStatistics ( $ ) {
  my $self = shift(@_);

  $self->{collectStatistics} = 0;
  delete $self->{statistics};
  delete $self->{stopWatch};
}

sub getStatistics ( $ ) {
  my $self = shift(@_);

  return $self->{statistics};
}

sub disconnect {
  my ($self) = @_;
  return $self->{backend}->disconnect();
}

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

  if (defined $self->{sql}) {
    Logging::debug("Unfinished SQL query: ", $self->{sql});
    delete $self->{sql};
  }

  Logging::trace(sprintf("SQL: %s: Params: %s", $sql, join(', ', @params)));

  if ($self->{collectStatistics})
  {
    $self->{stopWatch}->createMarker("sql");
  }

  my $res = $self->{backend}->execute($sql, $quiet, @params);

  if ($self->{collectStatistics})
  {
    $self->{statistics}->{sqlTime} += $self->{stopWatch}->getDiff("sql");
    $self->{stopWatch}->releaseMarker("sql");
  }

  $self->{sql} = $sql if $res;
  return $res;
}

sub execute {
  my ($self, $sql, @params) = @_;
  return $self->_execute($sql, 0, @params);
}

sub executeQuiet {
  my ($self, $sql, @params) = @_;
  return $self->_execute($sql, 1, @params);
}

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

  if ($self->{collectStatistics})
  {
    $self->{stopWatch}->createMarker("sql");
  }

  my $ret = $self->{backend}->rownum();

  if ($self->{collectStatistics})
  {
    $self->{statistics}->{sqlTime} += $self->{stopWatch}->getDiff("sql");
    $self->{stopWatch}->releaseMarker("sql");
  }

  return $ret;
}

sub execute_rownum {
  my ($self, $sql, @params) = @_;
  return if !$self->execute($sql, @params);
  return $self->rownum();
}

sub execute_rownum_quiet {
  my ($self, $sql, @params) = @_;
  return if !$self->executeQuiet($sql, @params);
  return $self->rownum();
}

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

  if ($self->{collectStatistics})
  {
    $self->{stopWatch}->createMarker("sql");
  }

  my $ret = $self->{backend}->fetchrow();

  if ($self->{collectStatistics})
  {
    $self->{statistics}->{sqlTime} += $self->{stopWatch}->getDiff("sql");
    $self->{stopWatch}->releaseMarker("sql");
  }

  return $ret;
}

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

  if ($self->{collectStatistics})
  {
    $self->{stopWatch}->createMarker("sql");
  }

  my $ret = $self->{backend}->fetchhash();

  if ($self->{collectStatistics})
  {
    $self->{statistics}->{sqlTime} += $self->{stopWatch}->getDiff("sql");
    $self->{stopWatch}->releaseMarker("sql");
  }

  return $ret;
}

sub finish {
  my ($self) = @_;
  delete $self->{sql};

  if ($self->{collectStatistics})
  {
    $self->{stopWatch}->createMarker("sql");
  }

  my $ret = $self->{backend}->finish();

  if ($self->{collectStatistics})
  {
    $self->{statistics}->{sqlTime} += $self->{stopWatch}->getDiff("sql");
    $self->{stopWatch}->releaseMarker("sql");
  }

  return $ret;
}

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

  if ($self->{collectStatistics})
  {
    $self->{stopWatch}->createMarker("sql");
  }

  my $ret = $self->{backend}->ping();

  if ($self->{collectStatistics})
  {
    $self->{statistics}->{sqlTime} += $self->{stopWatch}->getDiff("sql");
    $self->{stopWatch}->releaseMarker("sql");
  }

  return $ret;
}

sub getName {
  my ($self) = @_;
  return $self->{backend}->{name};
}

sub getPassword {
  my ($self) = @_;
  return $self->{backend}->{password};
}

sub getUser {
  my ($self) = @_;
  return $self->{backend}->{user};
}

sub getType {
  my ($self) = @_;
  return $self->{backend}->{type};
}

sub quote {
  my ($self, $sql) = @_;
  return $self->{backend}->quote($sql);

}

1;
