[ticket/10678] Add better support for Firebird, Oracle, and MSSQL

Allow ODBC connections for Firebird
Capitalize fixture tables and columns for Firebird
On database drop failure, drop all tables
Provide cleanup utilities for databases that cannot be dropped

PHPBB3-10678
This commit is contained in:
Patrick Webster 2012-02-28 06:18:24 -06:00
parent c0718fae96
commit d578eff712
4 changed files with 171 additions and 5 deletions

View file

@ -43,6 +43,25 @@ will run phpunit with the same parameters as in the shown test_config.php file:
PHPBB_TEST_DBNAME='database' PHPBB_TEST_DBUSER='user' \ PHPBB_TEST_DBNAME='database' PHPBB_TEST_DBUSER='user' \
PHPBB_TEST_DBPASSWD='password' phpunit PHPBB_TEST_DBPASSWD='password' phpunit
Special Database Cases
----------------------
In order to run tests on some of the databases that we support, it will be
necessary to provide a custom DSN string in test_config.php. This is only needed for
MSSQL 2000+ (PHP module), MSSQL via ODBC, and Firebird when PDO_Firebird does not work
on your system (https://bugs.php.net/bug.php?id=61183). The variable must be named $custom_dsn.
Examples:
Firebird using http://www.firebirdsql.org/en/odbc-driver/
$custom_dsn = "Driver={Firebird/InterBase(r) driver};dbname=$dbhost:$dbname";
MSSQL
$custom_dsn = "Driver={SQL Server Native Client 10.0};Server=$dbhost;Database=$dbname";
The other fields in test_config.php should be filled out as you would normally to connect
to that database in phpBB.
Additionally, you will need to be running the DbUnit fork from https://github.com/phpbb/dbunit/tree/phpbb.
Running Running
======= =======

View file

@ -0,0 +1,24 @@
<?php
/**
* Used for passing in information about the PDO driver
* since the PDO class reveals nothing about the DSN that
* the user provided.
*
* This is used in the custom PHPUnit ODBC driver
*/
class phpbb_database_connection_ODBC_PDO_wrapper extends PDO
{
//Name of the driver being used (i.e. mssql, firebird)
public $driver = '';
//Version number of driver since PDO::getAttribute(PDO::ATTR_CLIENT_VERSION) is pretty useless for this
public $version = 0;
function __construct($dbms, $version, $dsn, $user, $pass)
{
$this->driver = $dbms;
$this->version = (double)$version;
parent::__construct($dsn, $user, $pass);
}
}

View file

@ -28,6 +28,28 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test
); );
} }
public function createXMLDataSet($path)
{
$db_config = $this->get_database_config();
//Firebird requires table and column names to be uppercase
if($db_config['dbms'] == 'firebird')
{
$xml_data = file_get_contents($path);
$xml_data = preg_replace_callback('/(?:(<table name="))([a-z_]+)(?:(">))/', 'phpbb_database_test_case::to_upper', $xml_data);
$xml_data = preg_replace_callback('/(?:(<column>))([a-z_]+)(?:(<\/column>))/', 'phpbb_database_test_case::to_upper', $xml_data);
$temp = tmpfile();
fwrite($temp, $xml_data);
fseek($temp, 0);
$meta_data = stream_get_meta_data($temp);
$path = $meta_data['uri'];
}
return parent::createXMLDataSet($path);
}
public function get_test_case_helpers() public function get_test_case_helpers()
{ {
if (!$this->test_case_helpers) if (!$this->test_case_helpers)
@ -106,4 +128,9 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test
{ {
return new phpbb_database_test_connection_manager($config); return new phpbb_database_test_connection_manager($config);
} }
public static function to_upper($matches)
{
return $matches[1] . strtoupper($matches[2]) . $matches[3];
}
} }

View file

@ -8,6 +8,7 @@
*/ */
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_install.php'; require_once dirname(__FILE__) . '/../../phpBB/includes/functions_install.php';
require_once 'phpbb_database_connection_helper.php';
class phpbb_database_test_connection_manager class phpbb_database_test_connection_manager
{ {
@ -83,9 +84,37 @@ class phpbb_database_test_connection_manager
break; break;
} }
//These require different connection strings on the phpBB side than they do in PDO
//so you must provide a DSN string for ODBC separately
if($this->config['dbms'] == 'mssql' || $this->config['dbms'] == 'firebird')
{
if(!empty($this->config['custom_dsn']))
{
$dsn = 'odbc:' . $this->config['custom_dsn'];
}
}
try try
{ {
switch($this->config['dbms'])
{
case 'mssql':
case 'mssql_odbc':
$this->pdo = new phpbb_database_connection_ODBC_PDO_wrapper('mssql', 0, $dsn, $this->config['dbuser'], $this->config['dbpasswd']);
break;
case 'firebird':
if(!empty($this->config['custom_dsn']))
{
$this->pdo = new phpbb_database_connection_ODBC_PDO_wrapper('firebird', 0, $dsn, $this->config['dbuser'], $this->config['dbpasswd']);
break;
}
//Fall through if they're using the firebird PDO driver and not the generic ODBC driver
default:
$this->pdo = new PDO($dsn, $this->config['dbuser'], $this->config['dbpasswd']); $this->pdo = new PDO($dsn, $this->config['dbuser'], $this->config['dbpasswd']);
break;
}
} }
catch (PDOException $e) catch (PDOException $e)
{ {
@ -93,8 +122,7 @@ class phpbb_database_test_connection_manager
throw new Exception("Unable do connect to $cleaned_dsn using PDO with error: {$e->getMessage()}"); throw new Exception("Unable do connect to $cleaned_dsn using PDO with error: {$e->getMessage()}");
} }
// good for debug $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} }
/** /**
@ -125,12 +153,41 @@ class phpbb_database_test_connection_manager
} }
break; break;
case 'firebird':
$this->connect();
// Drop all of the tables
foreach ($this->get_tables() as $table)
{
$this->pdo->exec('DROP TABLE ' . $table);
}
$this->purge_extras();
break;
case 'oracle':
$this->connect();
// Drop all of the tables
foreach ($this->get_tables() as $table)
{
$this->pdo->exec('DROP TABLE ' . $table . ' CASCADE CONSTRAINTS');
}
$this->purge_extras();
break;
default: default:
$this->connect(false); $this->connect(false);
try try
{ {
$this->pdo->exec('DROP DATABASE ' . $this->config['dbname']); $this->pdo->exec('DROP DATABASE ' . $this->config['dbname']);
try
{
$this->pdo->exec('CREATE DATABASE ' . $this->config['dbname']);
}
catch (PDOException $e)
{
throw new Exception("Unable to re-create database: {$e->getMessage()}");
}
} }
catch (PDOException $e) catch (PDOException $e)
{ {
@ -139,9 +196,8 @@ class phpbb_database_test_connection_manager
{ {
$this->pdo->exec('DROP TABLE ' . $table); $this->pdo->exec('DROP TABLE ' . $table);
} }
$this->purge_extras();
} }
$this->pdo->exec('CREATE DATABASE ' . $this->config['dbname']);
break; break;
} }
} }
@ -317,4 +373,44 @@ class phpbb_database_test_connection_manager
throw new Exception($message); throw new Exception($message);
} }
} }
/**
* Removes extra objects from a database. This is for cases where dropping the database fails.
*/
public function purge_extras()
{
$this->ensure_connected(__METHOD__);
$queries = array();
switch ($this->config['dbms'])
{
case 'firebird':
$sql = 'SELECT RDB$GENERATOR_NAME
FROM RDB$GENERATORS
WHERE RDB$SYSTEM_FLAG = 0';
$result = $this->pdo->query($sql);
while ($row = $result->fetch(PDO::FETCH_NUM))
{
$queries[] = 'DROP GENERATOR ' . current($row);
}
break;
case 'oracle':
$sql = 'SELECT sequence_name
FROM USER_SEQUENCES';
$result = $this->pdo->query($sql);
while ($row = $result->fetch(PDO::FETCH_NUM))
{
$queries[] = 'DROP SEQUENCE ' . current($row);
}
break;
}
foreach($queries as $query)
{
$this->pdo->exec($query);
}
}
} }