Skip to content

Commit

Permalink
Dialect now configurable
Browse files Browse the repository at this point in the history
- Cf. issue #6.
- `Connection` constructor now correctly applies the desired dialect.
- Added tests targeting know cases for differences in dialects.
- Refactored `AbstractIntegrationTest` to allow additional customization of the initialization of the connection, installation of the database, and creation of the `EntityManager`.
  • Loading branch information
kafoso committed Dec 27, 2018
1 parent 6457b43 commit 389afcf
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 28 deletions.
10 changes: 9 additions & 1 deletion src/Driver/FirebirdInterbase/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Connection implements ConnectionInterface, ServerInfoAwareConnection
const DEFAULT_CHARSET = 'UTF-8';
const DEFAULT_BUFFERS = 0;
const DEFAULT_IS_PERSISTENT = true;
const DEFAULT_DIALECT = 0;

/**
* @var null|string
Expand All @@ -36,7 +37,7 @@ class Connection implements ConnectionInterface, ServerInfoAwareConnection
protected $buffers = 0;

/**
* @var string
* @var int
*/
protected $dialect = 0;

Expand Down Expand Up @@ -98,6 +99,13 @@ public function __construct(array $params, $username, $password, array $driverOp
if (isset($params['buffers']) && is_int($params['buffers']) && $params['buffers'] >= 0) {
$this->buffers = $params['buffers'];
}
$this->dialect = self::DEFAULT_DIALECT;
if (isset($params['dialect'])
&& is_int($params['dialect'])
&& $params['dialect'] >= 0
&& $params['dialect'] <= 3) {
$this->dialect = $params['dialect'];
}
$this->username = $username;
$this->password = $password;
if ($driverOptions) {
Expand Down
93 changes: 66 additions & 27 deletions tests/tests/Test/Integration/AbstractIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,47 @@

abstract class AbstractIntegrationTest extends \PHPUnit_Framework_TestCase
{
const DEFAULT_DATABASE_FILE_PATH = '/var/lib/firebird/2.5/data/music_library.fdb';
const DEFAULT_DATABASE_USERNAME = 'SYSDBA';
const DEFAULT_DATABASE_PASSWORD = '88fb9f307125cc397f70e59c749715e1';

protected $_entityManager;
protected $_platform;

public function setUp()
{
$cache = new \Doctrine\Common\Cache\ArrayCache;
$doctrineConfiguration = new Configuration;
$driverImpl = $doctrineConfiguration->newDefaultAnnotationDriver([ROOT_PATH . '/tests/resources/Test/Entity'], false);
$doctrineConfiguration->setMetadataDriverImpl($driverImpl);
$doctrineConfiguration->setProxyDir(ROOT_PATH . '/var/doctrine-proxies');
$doctrineConfiguration->setProxyNamespace('DoctrineFirebirdDriver\Proxies');
$doctrineConfiguration->setAutoGenerateProxyClasses(true);
$doctrineConfiguration = static::getSetUpDoctrineConfiguration();
$configurationArray = static::getSetUpDoctrineConfigurationArray();
static::installFirebirdDatabase($configurationArray);
$this->_entityManager = static::createEntityManager($doctrineConfiguration, $configurationArray);
$this->_platform = new FirebirdInterbasePlatform;
}

public function tearDown()
{
if ($this->_entityManager) {
$this->_entityManager->getConnection()->close();
}
}

$dbname = '/var/lib/firebird/2.5/data/music_library.fdb';
$username = 'SYSDBA';
$password = '88fb9f307125cc397f70e59c749715e1';
/**
* @return EntityManager
*/
protected static function createEntityManager(Configuration $configuration, array $configurationArray)
{
$doctrineConnection = new Connection(
[
'host' => 'localhost',
'dbname' => $dbname,
'user' => $username,
'password' => $password,
'charset' => 'UTF-8',
],
$configurationArray,
new FirebirdInterbase\Driver,
$doctrineConfiguration
$configuration
);
$doctrineConnection->setNestTransactionsWithSavepoints(true);
$this->_entityManager = EntityManager::create($doctrineConnection, $doctrineConfiguration);
return EntityManager::create($doctrineConnection, $configuration);
}

if (file_exists($dbname)) {
unlink($dbname); // Don't do this outside tests
protected static function installFirebirdDatabase(array $configurationArray)
{
if (file_exists($configurationArray['dbname'])) {
unlink($configurationArray['dbname']); // Don't do this outside tests
}

$cmd = sprintf(
Expand All @@ -49,20 +58,21 @@ public function setUp()
);
exec($cmd);

chmod($dbname, 0777);
chmod($configurationArray['dbname'], 0777);

$cmd = sprintf(
"isql-fb %s -input %s -password %s -user %s",
escapeshellarg($dbname),
escapeshellarg($configurationArray['dbname']),
escapeshellarg(ROOT_PATH . "/tests/resources/database_setup.sql"),
escapeshellarg($password),
escapeshellarg($username)
escapeshellarg($configurationArray['password']),
escapeshellarg($configurationArray['user'])
);
exec($cmd);

$this->_platform = new FirebirdInterbasePlatform;
}

/**
* @return string
*/
protected static function statementArrayToText(array $statements)
{
$statements = array_filter($statements, function($statement){
Expand All @@ -77,4 +87,33 @@ protected static function statementArrayToText(array $statements)
}
return "";
}

/**
* @return Configuration
*/
protected static function getSetUpDoctrineConfiguration()
{
$cache = new \Doctrine\Common\Cache\ArrayCache;
$doctrineConfiguration = new Configuration;
$driverImpl = $doctrineConfiguration->newDefaultAnnotationDriver([ROOT_PATH . '/tests/resources/Test/Entity'], false);
$doctrineConfiguration->setMetadataDriverImpl($driverImpl);
$doctrineConfiguration->setProxyDir(ROOT_PATH . '/var/doctrine-proxies');
$doctrineConfiguration->setProxyNamespace('DoctrineFirebirdDriver\Proxies');
$doctrineConfiguration->setAutoGenerateProxyClasses(true);
return $doctrineConfiguration;
}

/**
* @return array
*/
protected static function getSetUpDoctrineConfigurationArray(array $overrideConfigs = [])
{
return [
'host' => 'localhost',
'dbname' => static::DEFAULT_DATABASE_FILE_PATH,
'user' => static::DEFAULT_DATABASE_USERNAME,
'password' => static::DEFAULT_DATABASE_PASSWORD,
'charset' => 'UTF-8',
];
}
}
192 changes: 192 additions & 0 deletions tests/tests/Test/Integration/Doctrine/DBAL/Database/DialectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<?php
namespace Kafoso\DoctrineFirebirdDriver\Test\Integration\Doctrine\DBAL\Database;

use Kafoso\DoctrineFirebirdDriver\Test\Integration\AbstractIntegrationTest;

/**
* Tests based on table from:
* @link https://www.firebirdsql.org/pdfmanual/html/isql-dialects.html
*
* @runTestsInSeparateProcesses
*/
class DialectTest extends AbstractIntegrationTest
{
/**
* @override
*/
public function setUp()
{

}

public function testDialect0And3()
{
foreach ([0,3] as $dialect) {
$doctrineConfiguration = static::getSetUpDoctrineConfiguration();
$configurationArray = static::getSetUpDoctrineConfigurationArray([
'dialect' => 0
]);
static::installFirebirdDatabase($configurationArray);
$entityManager = static::createEntityManager($doctrineConfiguration, $configurationArray);
$connection = $entityManager->getConnection();

$stmt = $connection->prepare("SELECT CAST(CAST('2018-01-01' AS DATE) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertSame(100, strlen($result['TXT']));
$this->assertStringStartsWith("2018-01-01", $result['TXT']);

$stmt = $connection->prepare("SELECT CAST(CAST('2018-01-01 00:00:00' AS TIMESTAMP) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertSame(100, strlen($result['TXT']));
$this->assertSame("2018-01-01 00:00:00.0000", rtrim($result['TXT']));

$stmt = $connection->prepare("SELECT CAST(CAST('00:00:00' AS TIME) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertSame(100, strlen($result['TXT']));
$this->assertSame("00:00:00.0000", rtrim($result['TXT']));

$stmt = $connection->prepare("SELECT a.\"ID\" FROM Album AS a");
$stmt->execute();
$result = $stmt->fetch();
$this->assertInternalType("array", $result);
$this->assertArrayHasKey("ID", $result);
$this->assertSame(1, $result["ID"]);

$stmt = $connection->prepare("SELECT 1/3 AS NUMBER FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertInternalType("array", $result);
$this->assertArrayHasKey("NUMBER", $result);
$this->assertInternalType("integer", $result["NUMBER"]);
$this->assertSame(0, $result["NUMBER"]);

$entityManager->getConnection()->close();
}
}

public function testDialect1()
{
$doctrineConfiguration = static::getSetUpDoctrineConfiguration();
$configurationArray = static::getSetUpDoctrineConfigurationArray([
'dialect' => 1
]);
static::installFirebirdDatabase($configurationArray);
$entityManager = static::createEntityManager($doctrineConfiguration, $configurationArray);
$connection = $entityManager->getConnection();

$stmt = $connection->prepare("SELECT CAST(CAST('2018-01-01' AS DATE) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertSame(100, strlen($result['TXT']));
$this->assertStringStartsWith("1-JAN-2018", $result['TXT']);

$stmt = $connection->prepare("SELECT CAST(CAST('2018-01-01 00:00:00' AS TIMESTAMP) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertSame(100, strlen($result['TXT']));
$this->assertSame("1-JAN-2018", rtrim($result['TXT']));

$stmt = $connection->prepare("SELECT CAST(CAST('00:00:00' AS TIME) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
try {
$stmt->execute();
} catch (\Throwable $t) {
$this->assertSame('Doctrine\DBAL\Exception\SyntaxErrorException', get_class($t));
$this->assertStringStartsWith('Error -104: An exception occurred while executing ', $t->getMessage());
$this->assertInternalType("object", $t->getPrevious());
$this->assertSame('Kafoso\DoctrineFirebirdDriver\Driver\FirebirdInterbase\Exception', get_class($t->getPrevious()));
$this->assertStringStartsWith('Dynamic SQL Error SQL error code = -104 Client SQL dialect 1 does not support reference to TIME datatype', $t->getPrevious()->getMessage());
}

$stmt = $connection->prepare("SELECT a.\"ID\" FROM Album AS a");
try {
$stmt->execute();
} catch (\Throwable $t) {
$this->assertSame('Doctrine\DBAL\Exception\SyntaxErrorException', get_class($t));
$this->assertStringStartsWith('Error -104: An exception occurred while executing \'', $t->getMessage());
$this->assertInternalType("object", $t->getPrevious());
$this->assertSame('Kafoso\DoctrineFirebirdDriver\Driver\FirebirdInterbase\Exception', get_class($t->getPrevious()));
$this->assertStringStartsWith('Dynamic SQL Error SQL error code = -104 Token unknown - line 1, column 10 "ID"', $t->getPrevious()->getMessage());
}

$stmt = $connection->prepare("SELECT 1/3 AS NUMBER FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertInternalType("array", $result);
$this->assertArrayHasKey("NUMBER", $result);
$this->assertInternalType("float", $result["NUMBER"]);
$this->assertSame("0.33333333", number_format($result["NUMBER"], 8));

$entityManager->getConnection()->close();
}

public function testDialect2()
{
$doctrineConfiguration = static::getSetUpDoctrineConfiguration();
$configurationArray = static::getSetUpDoctrineConfigurationArray([
'dialect' => 2
]);
static::installFirebirdDatabase($configurationArray);
$entityManager = static::createEntityManager($doctrineConfiguration, $configurationArray);
$connection = $entityManager->getConnection();

$stmt = $connection->prepare("SELECT CAST(CAST('2018-01-01' AS DATE) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
try {
$stmt->execute();
} catch (\Throwable $t) {
$this->assertSame('Doctrine\DBAL\Exception\SyntaxErrorException', get_class($t));
$this->assertStringStartsWith('Error -104: An exception occurred while executing \'', $t->getMessage());
$this->assertInternalType("object", $t->getPrevious());
$this->assertSame('Kafoso\DoctrineFirebirdDriver\Driver\FirebirdInterbase\Exception', get_class($t->getPrevious()));
$this->assertStringStartsWith('Dynamic SQL Error SQL error code = -104 DATE must be changed to TIMESTAMP', $t->getPrevious()->getMessage());
}

$stmt = $connection->prepare("SELECT CAST(CAST('2018-01-01 00:00:00' AS TIMESTAMP) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertSame(100, strlen($result['TXT']));
$this->assertSame("2018-01-01 00:00:00.0000", rtrim($result['TXT']));

$stmt = $connection->prepare("SELECT CAST(CAST('00:00:00' AS TIME) AS CHAR(25)) AS TXT FROM RDB\$DATABASE");
try {
$stmt->execute();
} catch (\Throwable $t) {
$this->assertSame('Doctrine\DBAL\Exception\SyntaxErrorException', get_class($t));
$this->assertStringStartsWith('Error -104: An exception occurred while executing ', $t->getMessage());
$this->assertInternalType("object", $t->getPrevious());
$this->assertSame('Kafoso\DoctrineFirebirdDriver\Driver\FirebirdInterbase\Exception', get_class($t->getPrevious()));
$this->assertStringStartsWith('Dynamic SQL Error SQL error code = -104 Client SQL dialect 1 does not support reference to TIME datatype', $t->getPrevious()->getMessage());
}

$stmt = $connection->prepare("SELECT a.\"ID\" FROM Album AS a");
try {
$stmt->execute();
} catch (\Throwable $t) {
$this->assertSame('Doctrine\DBAL\Exception\SyntaxErrorException', get_class($t));
$this->assertStringStartsWith('Error -104: An exception occurred while executing \'', $t->getMessage());
$this->assertInternalType("object", $t->getPrevious());
$this->assertSame('Kafoso\DoctrineFirebirdDriver\Driver\FirebirdInterbase\Exception', get_class($t->getPrevious()));
$this->assertStringStartsWith('Dynamic SQL Error SQL error code = -104 a string constant is delimited by double quotes', $t->getPrevious()->getMessage());
}

$stmt = $connection->prepare("SELECT 1/3 AS NUMBER FROM RDB\$DATABASE");
$stmt->execute();
$result = $stmt->fetch();
$this->assertInternalType("array", $result);
$this->assertArrayHasKey("NUMBER", $result);
$this->assertInternalType("integer", $result["NUMBER"]);
$this->assertSame(0, $result["NUMBER"]);

$entityManager->getConnection()->close();
}

/**
* @inheritDoc
*/
protected static function getSetUpDoctrineConfigurationArray(array $overrideConfigs = [])
{
return array_merge(parent::getSetUpDoctrineConfigurationArray(), $overrideConfigs);
}
}

0 comments on commit 389afcf

Please sign in to comment.