Skip to content

Commit f211371

Browse files
author
Yevhen Miroshnychenko
authored
Merge pull request magento#3742 from magento-thunder/MAGETWO-94241
Fixed issues: - MAGETWO-94241: Mysql reconnect does not work
2 parents 5276b27 + 192c6d1 commit f211371

File tree

3 files changed

+192
-44
lines changed

3 files changed

+192
-44
lines changed

dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Magento\Framework\App\ResourceConnection;
99
use Magento\TestFramework\Helper\CacheCleaner;
1010
use Magento\Framework\DB\Ddl\Table;
11+
use Magento\TestFramework\Helper\Bootstrap;
1112

1213
class MysqlTest extends \PHPUnit\Framework\TestCase
1314
{
@@ -19,7 +20,7 @@ class MysqlTest extends \PHPUnit\Framework\TestCase
1920
protected function setUp()
2021
{
2122
set_error_handler(null);
22-
$this->resourceConnection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
23+
$this->resourceConnection = Bootstrap::getObjectManager()
2324
->get(ResourceConnection::class);
2425
CacheCleaner::cleanAll();
2526
}
@@ -40,7 +41,6 @@ public function testWaitTimeout()
4041
$this->markTestSkipped('This test is for \Magento\Framework\DB\Adapter\Pdo\Mysql');
4142
}
4243
try {
43-
$defaultWaitTimeout = $this->getWaitTimeout();
4444
$minWaitTimeout = 1;
4545
$this->setWaitTimeout($minWaitTimeout);
4646
$this->assertEquals($minWaitTimeout, $this->getWaitTimeout(), 'Wait timeout was not changed');
@@ -49,17 +49,8 @@ public function testWaitTimeout()
4949
sleep($minWaitTimeout + 1);
5050
$result = $this->executeQuery('SELECT 1');
5151
$this->assertInstanceOf(\Magento\Framework\DB\Statement\Pdo\Mysql::class, $result);
52-
// Restore wait_timeout
53-
$this->setWaitTimeout($defaultWaitTimeout);
54-
$this->assertEquals(
55-
$defaultWaitTimeout,
56-
$this->getWaitTimeout(),
57-
'Default wait timeout was not restored'
58-
);
59-
} catch (\Exception $e) {
60-
// Reset connection on failure to restore global variables
52+
} finally {
6153
$this->getDbAdapter()->closeConnection();
62-
throw $e;
6354
}
6455
}
6556

@@ -87,30 +78,14 @@ private function setWaitTimeout($waitTimeout)
8778
/**
8879
* Execute SQL query and return result statement instance
8980
*
90-
* @param string $sql
91-
* @return \Zend_Db_Statement_Interface
92-
* @throws \Exception
81+
* @param $sql
82+
* @return void|\Zend_Db_Statement_Pdo
83+
* @throws \Magento\Framework\Exception\LocalizedException
84+
* @throws \Zend_Db_Adapter_Exception
9385
*/
9486
private function executeQuery($sql)
9587
{
96-
/**
97-
* Suppress PDO warnings to work around the bug https://bugs.php.net/bug.php?id=63812
98-
*/
99-
$phpErrorReporting = error_reporting();
100-
/** @var $pdoConnection \PDO */
101-
$pdoConnection = $this->getDbAdapter()->getConnection();
102-
$pdoWarningsEnabled = $pdoConnection->getAttribute(\PDO::ATTR_ERRMODE) & \PDO::ERRMODE_WARNING;
103-
if (!$pdoWarningsEnabled) {
104-
error_reporting($phpErrorReporting & ~E_WARNING);
105-
}
106-
try {
107-
$result = $this->getDbAdapter()->query($sql);
108-
error_reporting($phpErrorReporting);
109-
} catch (\Exception $e) {
110-
error_reporting($phpErrorReporting);
111-
throw $e;
112-
}
113-
return $result;
88+
return $this->getDbAdapter()->query($sql);
11489
}
11590

11691
/**

lib/internal/Magento/Framework/DB/Statement/Pdo/Mysql.php

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
namespace Magento\Framework\DB\Statement\Pdo;
7+
8+
use Magento\Framework\DB\Statement\Parameter;
69

710
/**
811
* Mysql DB Statement
912
*
1013
* @author Magento Core Team <[email protected]>
1114
*/
12-
namespace Magento\Framework\DB\Statement\Pdo;
13-
14-
use Magento\Framework\DB\Statement\Parameter;
15-
1615
class Mysql extends \Zend_Db_Statement_Pdo
1716
{
17+
1818
/**
19-
* Executes statement with binding values to it.
20-
* Allows transferring specific options to DB driver.
19+
* Executes statement with binding values to it. Allows transferring specific options to DB driver.
2120
*
2221
* @param array $params Array of values to bind to parameter placeholders.
2322
* @return bool
@@ -61,11 +60,9 @@ public function _executeWithBinding(array $params)
6160
$statement->bindParam($paramName, $bindValues[$name], $dataType, $length, $driverOptions);
6261
}
6362

64-
try {
63+
return $this->tryExecute(function () use ($statement) {
6564
return $statement->execute();
66-
} catch (\PDOException $e) {
67-
throw new \Zend_Db_Statement_Exception($e->getMessage(), (int)$e->getCode(), $e);
68-
}
65+
});
6966
}
7067

7168
/**
@@ -90,7 +87,29 @@ public function _execute(array $params = null)
9087
if ($specialExecute) {
9188
return $this->_executeWithBinding($params);
9289
} else {
93-
return parent::_execute($params);
90+
return $this->tryExecute(function () use ($params) {
91+
return $params !== null ? $this->_stmt->execute($params) : $this->_stmt->execute();
92+
});
93+
}
94+
}
95+
96+
/**
97+
* Executes query and avoid warnings.
98+
*
99+
* @param callable $callback
100+
* @return bool
101+
* @throws \Zend_Db_Statement_Exception
102+
*/
103+
private function tryExecute($callback)
104+
{
105+
$previousLevel = error_reporting(\E_ERROR); // disable warnings for PDO bugs #63812, #74401
106+
try {
107+
return $callback();
108+
} catch (\PDOException $e) {
109+
$message = sprintf('%s, query was: %s', $e->getMessage(), $this->_stmt->queryString);
110+
throw new \Zend_Db_Statement_Exception($message, (int)$e->getCode(), $e);
111+
} finally {
112+
error_reporting($previousLevel);
94113
}
95114
}
96115
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Framework\DB\Test\Unit\DB\Statement;
8+
9+
use Magento\Framework\DB\Statement\Parameter;
10+
use Magento\Framework\DB\Statement\Pdo\Mysql;
11+
use PHPUnit\Framework\MockObject\MockObject;
12+
use PHPUnit\Framework\TestCase;
13+
14+
/**
15+
* @inheritdoc
16+
*/
17+
class MysqlTest extends TestCase
18+
{
19+
/**
20+
* @var \Zend_Db_Adapter_Abstract|MockObject
21+
*/
22+
private $adapterMock;
23+
24+
/**
25+
* @var \PDO|MockObject
26+
*/
27+
private $pdoMock;
28+
29+
/**
30+
* @var \Zend_Db_Profiler|MockObject
31+
*/
32+
private $zendDbProfilerMock;
33+
34+
/**
35+
* @var \PDOStatement|MockObject
36+
*/
37+
private $pdoStatementMock;
38+
39+
/**
40+
* @inheritdoc
41+
*/
42+
public function setUp()
43+
{
44+
$this->adapterMock = $this->getMockForAbstractClass(
45+
\Zend_Db_Adapter_Abstract::class,
46+
[],
47+
'',
48+
false,
49+
true,
50+
true,
51+
['getConnection', 'getProfiler']
52+
);
53+
$this->pdoMock = $this->createMock(\PDO::class);
54+
$this->adapterMock->expects($this->once())
55+
->method('getConnection')
56+
->willReturn($this->pdoMock);
57+
$this->zendDbProfilerMock = $this->createMock(\Zend_Db_Profiler::class);
58+
$this->adapterMock->expects($this->once())
59+
->method('getProfiler')
60+
->willReturn($this->zendDbProfilerMock);
61+
$this->pdoStatementMock = $this->createMock(\PDOStatement::class);
62+
}
63+
64+
public function testExecuteWithoutParams()
65+
{
66+
$query = 'SET @a=1;';
67+
$this->pdoMock->expects($this->once())
68+
->method('prepare')
69+
->with($query)
70+
->willReturn($this->pdoStatementMock);
71+
$this->pdoStatementMock->expects($this->once())
72+
->method('execute');
73+
(new Mysql($this->adapterMock, $query))->_execute();
74+
}
75+
76+
public function testExecuteWhenThrowPDOException()
77+
{
78+
$this->expectException(\Zend_Db_Statement_Exception::class);
79+
$this->expectExceptionMessage('test message, query was:');
80+
$errorReporting = error_reporting();
81+
$query = 'SET @a=1;';
82+
$this->pdoMock->expects($this->once())
83+
->method('prepare')
84+
->with($query)
85+
->willReturn($this->pdoStatementMock);
86+
$this->pdoStatementMock->expects($this->once())
87+
->method('execute')
88+
->willThrowException(new \PDOException('test message'));
89+
90+
$this->assertEquals($errorReporting, error_reporting(), 'Error report level was\'t restored');
91+
92+
(new Mysql($this->adapterMock, $query))->_execute();
93+
}
94+
95+
public function testExecuteWhenParamsAsPrimitives()
96+
{
97+
$params = [':param1' => 'value1', ':param2' => 'value2'];
98+
$query = 'UPDATE `some_table1` SET `col1`=\'val1\' WHERE `param1`=\':param1\' AND `param2`=\':param2\';';
99+
$this->pdoMock->expects($this->once())
100+
->method('prepare')
101+
->with($query)
102+
->willReturn($this->pdoStatementMock);
103+
$this->pdoStatementMock->expects($this->never())
104+
->method('bindParam');
105+
$this->pdoStatementMock->expects($this->once())
106+
->method('execute')
107+
->with($params);
108+
109+
(new Mysql($this->adapterMock, $query))->_execute($params);
110+
}
111+
112+
public function testExecuteWhenParamsAsParameterObject()
113+
{
114+
$param1 = $this->createMock(Parameter::class);
115+
$param1Value = 'SomeValue';
116+
$param1DataType = 'dataType';
117+
$param1Length = '9';
118+
$param1DriverOptions = 'some driver options';
119+
$param1->expects($this->once())
120+
->method('getIsBlob')
121+
->willReturn(false);
122+
$param1->expects($this->once())
123+
->method('getDataType')
124+
->willReturn($param1DataType);
125+
$param1->expects($this->once())
126+
->method('getLength')
127+
->willReturn($param1Length);
128+
$param1->expects($this->once())
129+
->method('getDriverOptions')
130+
->willReturn($param1DriverOptions);
131+
$param1->expects($this->once())
132+
->method('getValue')
133+
->willReturn($param1Value);
134+
$params = [
135+
':param1' => $param1,
136+
':param2' => 'value2',
137+
];
138+
$query = 'UPDATE `some_table1` SET `col1`=\'val1\' WHERE `param1`=\':param1\' AND `param2`=\':param2\';';
139+
$this->pdoMock->expects($this->once())
140+
->method('prepare')
141+
->with($query)
142+
->willReturn($this->pdoStatementMock);
143+
$this->pdoStatementMock->expects($this->exactly(2))
144+
->method('bindParam')
145+
->withConsecutive(
146+
[':param1', $param1Value, $param1DataType, $param1Length, $param1DriverOptions],
147+
[':param2', 'value2', \PDO::PARAM_STR, null, null]
148+
);
149+
$this->pdoStatementMock->expects($this->once())
150+
->method('execute');
151+
152+
(new Mysql($this->adapterMock, $query))->_execute($params);
153+
}
154+
}

0 commit comments

Comments
 (0)