Added support for cloning a db connection

improved fix #14020
fixes #13890

https://github.com/yiisoft/yii2/pull/14020/files#r115185865

close #14121
This commit is contained in:
Carsten Brandt
2017-05-08 10:31:34 +02:00
parent 80d6f7323a
commit 5ed6910e1c
5 changed files with 67 additions and 17 deletions

View File

@ -46,7 +46,7 @@ Yii Framework 2 Change Log
- Bug #13790: Fixed error in `\yii\widgets\MaskedInput` JavaScript by raising version required (samdark) - Bug #13790: Fixed error in `\yii\widgets\MaskedInput` JavaScript by raising version required (samdark)
- Bug #13807: Fixed `yii\db\QueryBuilder` to inherit subquery params when building a `INSERT INTO ... SELECT` query (sergeymakinen) - Bug #13807: Fixed `yii\db\QueryBuilder` to inherit subquery params when building a `INSERT INTO ... SELECT` query (sergeymakinen)
- Bug #13848: `yii\di\Instance::ensure()` wasn't throwing an exception when `$type` is specified and `$reference` object isn't instance of `$type` (c-jonua) - Bug #13848: `yii\di\Instance::ensure()` wasn't throwing an exception when `$type` is specified and `$reference` object isn't instance of `$type` (c-jonua)
- Bug #13890: DbTarget log transaction bug (shirase) - Bug #13890: `yii\log\DbTarget` log messages where not written when a database transaction was rolled back, added support for cloning a `yii\db\Connection` (shirase, cebe)
- Bug #13901: Fixed passing unused parameter to `formatMessage()` call in `\yii\validators\IpValidator` (Kolyunya) - Bug #13901: Fixed passing unused parameter to `formatMessage()` call in `\yii\validators\IpValidator` (Kolyunya)
- Bug #13961: Fixed `unserialize()` error during RBAC rule retrieving from PostgreSQL DBMS (vsguts) - Bug #13961: Fixed `unserialize()` error during RBAC rule retrieving from PostgreSQL DBMS (vsguts)
- Bug #14012: `yii\db\pgsql\Schema::findViewNames()` was skipping materialized views (insolita) - Bug #14012: `yii\db\pgsql\Schema::findViewNames()` was skipping materialized views (insolita)

View File

@ -1080,4 +1080,18 @@ class Connection extends Component
$this->close(); $this->close();
return array_keys((array) $this); return array_keys((array) $this);
} }
/**
* Reset the connection after cloning.
*/
public function __clone()
{
parent::__clone();
$this->_master = false;
$this->_slave = false;
$this->pdo = null;
$this->_schema = null;
$this->_transaction = null;
}
} }

View File

@ -61,9 +61,9 @@ class DbTarget extends Target
public function export() public function export()
{ {
if ($this->db->getTransaction()) { if ($this->db->getTransaction()) {
// create new database connection, if there is an open transaction
// to ensure insert statement is not affected by a rollback
$this->db = clone $this->db; $this->db = clone $this->db;
$this->db->pdo = null;
$this->db->open();
} }
$tableName = $this->db->quoteTableName($this->logTable); $tableName = $this->db->quoteTableName($this->logTable);

View File

@ -331,4 +331,41 @@ abstract class ConnectionTest extends DatabaseTestCase
} }
$this->assertTrue($thrown, 'An exception should have been thrown by the command.'); $this->assertTrue($thrown, 'An exception should have been thrown by the command.');
} }
/**
* Ensure database connection is reset on when a connection is cloned.
* Make sure each connection element has its own PDO instance i.e. own connection to the DB.
* Also transaction elements should not be shared between two connections.
*/
public function testClone()
{
$connection = $this->getConnection(true, false);
$this->assertNull($connection->transaction);
$this->assertNull($connection->pdo);
$connection->open();
$this->assertNull($connection->transaction);
$this->assertNotNull($connection->pdo);
$conn2 = clone $connection;
$this->assertNull($connection->transaction);
$this->assertNotNull($connection->pdo);
$this->assertNull($conn2->transaction);
$this->assertNull($conn2->pdo);
$connection->beginTransaction();
$this->assertNotNull($connection->transaction);
$this->assertNotNull($connection->pdo);
$this->assertNull($conn2->transaction);
$this->assertNull($conn2->pdo);
$conn3 = clone $connection;
$this->assertNotNull($connection->transaction);
$this->assertNotNull($connection->pdo);
$this->assertNull($conn3->transaction);
$this->assertNull($conn3->pdo);
}
} }

View File

@ -37,7 +37,7 @@ abstract class DbTargetTest extends TestCase
'db' => static::getConnection(), 'db' => static::getConnection(),
'log' => [ 'log' => [
'targets' => [ 'targets' => [
[ 'db' => [
'class' => 'yii\log\DbTarget', 'class' => 'yii\log\DbTarget',
'levels' => ['warning'], 'levels' => ['warning'],
'logTable' => self::$logTable, 'logTable' => self::$logTable,
@ -58,9 +58,9 @@ abstract class DbTargetTest extends TestCase
} }
} }
public static function setUpBeforeClass() public function setUp()
{ {
parent::setUpBeforeClass(); parent::setUp();
$databases = static::getParam('databases'); $databases = static::getParam('databases');
static::$database = $databases[static::$driverName]; static::$database = $databases[static::$driverName];
$pdo_database = 'pdo_' . static::$driverName; $pdo_database = 'pdo_' . static::$driverName;
@ -72,20 +72,14 @@ abstract class DbTargetTest extends TestCase
static::runConsoleAction('migrate/up', ['migrationPath' => '@yii/log/migrations/', 'interactive' => false]); static::runConsoleAction('migrate/up', ['migrationPath' => '@yii/log/migrations/', 'interactive' => false]);
} }
public static function tearDownAfterClass() public function tearDown()
{ {
self::getConnection()->createCommand()->truncateTable(self::$logTable)->execute();
static::runConsoleAction('migrate/down', ['migrationPath' => '@yii/log/migrations/', 'interactive' => false]); static::runConsoleAction('migrate/down', ['migrationPath' => '@yii/log/migrations/', 'interactive' => false]);
if (static::$db) { if (static::$db) {
static::$db->close(); static::$db->close();
} }
Yii::$app = null;
parent::tearDownAfterClass();
}
protected function tearDown()
{
parent::tearDown(); parent::tearDown();
self::getConnection()->createCommand()->truncateTable(self::$logTable)->execute();
} }
/** /**
@ -159,6 +153,11 @@ abstract class DbTargetTest extends TestCase
$logger->messages[] = $messsageData; $logger->messages[] = $messsageData;
$logger->flush(true); $logger->flush(true);
// current db connection should still have a transaction
$this->assertNotNull($db->transaction);
// log db connection should not have transaction
$this->assertNull(Yii::$app->log->targets['db']->db->transaction);
$tx->rollBack(); $tx->rollBack();
$query = (new Query())->select('COUNT(*)')->from(self::$logTable)->where(['category' => 'test', 'message' => 'test']); $query = (new Query())->select('COUNT(*)')->from(self::$logTable)->where(['category' => 'test', 'message' => 'test']);