Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
55.00% covered (warning)
55.00%
11 / 20
CRAP
51.35% covered (warning)
51.35%
38 / 74
MySQLConnection
0.00% covered (danger)
0.00%
0 / 1
55.00% covered (warning)
55.00%
11 / 20
204.26
51.35% covered (warning)
51.35%
38 / 74
 __construct
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 getDatabase
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 connect
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 12
 useDatabase
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 query
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
6 / 6
 multiQuery
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 execute
0.00% covered (danger)
0.00%
0 / 1
3.03
85.71% covered (success)
85.71%
6 / 7
 executeStoredProcedure
0.00% covered (danger)
0.00%
0 / 1
2.06
75.00% covered (success)
75.00%
3 / 4
 handleQueryError
0.00% covered (danger)
0.00%
0 / 1
19.85
27.27% covered (danger)
27.27%
3 / 11
 escape
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getSequence
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
7 / 7
 startTransaction
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 commit
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 rollback
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 enableForeignKeyChecks
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 disableForeignKeyChecks
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 getDatabaseMetaData
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getMigrationStatementFactory
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getCreateStatementFactory
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getStoredProcedureFactory
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
<?php
declare(strict_types = 1);
namespace Siesta\Driver\MySQL;
use Siesta\Database\Connection;
use Siesta\Database\ConnectionData;
use Siesta\Database\CreateStatementFactory;
use Siesta\Database\Exception\CannotBeNullException;
use Siesta\Database\Exception\ConnectException;
use Siesta\Database\Exception\ForeignKeyConstraintFailedException;
use Siesta\Database\Exception\SQLException;
use Siesta\Database\Exception\TableAlreadyExistsException;
use Siesta\Database\Exception\TableDoesNotExistException;
use Siesta\Database\Exception\UniqueConstraintViolationException;
use Siesta\Database\MetaData\DatabaseMetaData;
use Siesta\Database\MigrationStatementFactory;
use Siesta\Database\ResultSet;
use Siesta\Database\StoredProcedureFactory;
use Siesta\Driver\MySQL\MetaData\MySQLDatabase;
/**
 * @author Gregor Müller
 */
class MySQLConnection implements Connection
{
    const NAME = "mysql";
    /**
     * @var \mysqli
     */
    private $connection;
    /**
     * @var string
     */
    private $database;
    /**
     * @param ConnectionData $connectionData
     *
     * @throws ConnectException
     */
    public function __construct(ConnectionData $connectionData)
    {
        $this->connect($connectionData);
    }
    /**
     *
     */
    public function getDatabase() : string
    {
        return $this->database;
    }
    /**
     * @param ConnectionData $connectionData
     *
     * @throws ConnectException
     */
    public function connect(ConnectionData $connectionData)
    {
        $this->database = $connectionData->database;
        // connect
        $this->connection = @new \mysqli ($connectionData->host, $connectionData->user, $connectionData->password, $connectionData->database, $connectionData->port);
        // database does not exist . create it
        if ($this->connection->connect_errno === 1049) {
            $this->connection = @new \mysqli ($connectionData->host, $connectionData->user, $connectionData->password, null, $connectionData->port);
        }
        // check for errors
        if ($this->connection->connect_errno) {
            throw new ConnectException ($connectionData, "Can't connect to " . $connectionData->host . " : " . $this->connection->connect_error, $this->connection->connect_errno);
        }
        // switch character set
        if ($connectionData->charSet) {
            $this->connection->set_charset($connectionData->charSet);
        }
        $pcs = implode(";", $connectionData->postConnectStatementList);
        if ($pcs) {
            $this->multiQuery($pcs);
        }
    }
    /**
     * @param string $name
     */
    public function useDatabase(string $name)
    {
        $this->database = $name;
        $this->connection->query("USE " . $name);
    }
    /**
     * @param string $query
     *
     * @throws CannotBeNullException
     * @throws ForeignKeyConstraintFailedException
     * @throws SQLException
     * @throws TableAlreadyExistsException
     * @throws TableDoesNotExistException
     * @throws UniqueConstraintViolationException
     * @return ResultSet
     */
    public function query(string $query) : ResultSet
    {
        $result = $this->connection->query($query);
        if ($result === false) {
            $this->handleQueryError($this->connection->errno, $this->connection->error, $query);
        }
        if ($result === true) {
            return new MySQLEmptyResult();
        }
        return new MySQLSimpleResultSet($result);
    }
    /**
     * @param string $query
     *
     * @return ResultSet
     * @throws CannotBeNullException
     * @throws ForeignKeyConstraintFailedException
     * @throws SQLException
     * @throws TableAlreadyExistsException
     * @throws TableDoesNotExistException
     * @throws UniqueConstraintViolationException
     */
    public function multiQuery(string $query) : ResultSet
    {
        $result = $this->connection->multi_query($query);
        if (!$result) {
            $this->handleQueryError($this->connection->errno, $this->connection->error, $query);
        }
        return new MySQLMultiQueryResultSet($this->connection);
    }
    /**
     * execute an sql command and free resulSet.
     *
     * @param string $query
     *
     * @throws CannotBeNullException
     * @throws ForeignKeyConstraintFailedException
     * @throws SQLException
     * @throws TableAlreadyExistsException
     * @throws TableDoesNotExistException
     * @throws UniqueConstraintViolationException
     */
    public function execute(string $query)
    {
        $result = $this->connection->multi_query($query);
        if (!$result) {
            $this->handleQueryError($this->connection->errno, $this->connection->error, $query);
        }
        while ($this->connection->more_results()) {
            $this->connection->next_result();
            $this->connection->use_result();
        }
    }
    /**
     * @param $query
     *
     * @throws CannotBeNullException
     * @throws ForeignKeyConstraintFailedException
     * @throws SQLException
     * @throws TableAlreadyExistsException
     * @throws TableDoesNotExistException
     * @throws UniqueConstraintViolationException
     * @return ResultSet
     */
    public function executeStoredProcedure(string $query) : ResultSet
    {
        $result = $this->connection->multi_query($query);
        if (!$result) {
            $this->handleQueryError($this->connection->errno, $this->connection->error, $query);
        }
        return new MySQLMultiQueryResultSet($this->connection);
    }
    /**
     * @param int $errorNumber
     * @param string $error
     * @param string $sql
     *
     * @throws CannotBeNullException
     * @throws ForeignKeyConstraintFailedException
     * @throws SQLException
     * @throws TableAlreadyExistsException
     * @throws TableDoesNotExistException
     * @throws UniqueConstraintViolationException
     */
    private function handleQueryError(int $errorNumber, string $error, string $sql)
    {
        switch ($errorNumber) {
            case 1048:
                throw new CannotBeNullException($error, $errorNumber, $sql);
            case 1050:
                throw new TableAlreadyExistsException($error, $errorNumber, $sql);
            case 1062:
                throw new UniqueConstraintViolationException($error, $errorNumber, $sql);
            case 1146:
                throw new TableDoesNotExistException($error, $errorNumber, $sql);
            case 1451:
                throw new ForeignKeyConstraintFailedException($error, $errorNumber, $sql);
            default:
                throw new SQLException($error, $errorNumber, $sql);
        }
    }
    /**
     * @param string $value
     *
     * @return string
     */
    public function escape(string $value)
    {
        return $this->connection->real_escape_string($value);
    }
    /**
     * @param string $technicalName
     *
     * @return int
     */
    public function getSequence(string $technicalName)
    {
        $result = $this->executeStoredProcedure("CALL `SEQUENCER_GETSEQUENCE`('$technicalName')");
        $sequence = null;
        while ($result->hasNext()) {
            $sequence = $result->getIntegerValue("@sequence:=SEQUENCER.SEQ");
        }
        $result->close();
        $r = ($sequence) ? $sequence : 1;
        return ($sequence) ? $sequence : 1;
    }
    public function startTransaction()
    {
        $this->connection->autocommit(false);
    }
    public function commit()
    {
        $this->connection->commit();
        $this->connection->autocommit(true);
    }
    public function rollback()
    {
        $this->connection->rollback();
        $this->connection->autocommit(true);
    }
    public function enableForeignKeyChecks()
    {
        $this->query("set foreign_key_checks=1");
    }
    public function disableForeignKeyChecks()
    {
        $this->query("set foreign_key_checks=0");
    }
    /**
     * @param string|null $databaseName
     *
     * @return DatabaseMetaData
     */
    public function getDatabaseMetaData(string $databaseName = null) : DatabaseMetaData
    {
        return new MySQLDatabase($this, $databaseName);
    }
    /**
     * @return MigrationStatementFactory
     */
    public function getMigrationStatementFactory() : MigrationStatementFactory
    {
        return new MySQLMigrationStatementFactory();
    }
    /**
     * @return CreateStatementFactory
     */
    public function getCreateStatementFactory() : CreateStatementFactory
    {
        return new MySQLCreateStatementFactory();
    }
    /**
     * @return StoredProcedureFactory
     */
    public function getStoredProcedureFactory() : StoredProcedureFactory
    {
        return new MySQLStoredProcedureFactory();
    }
}