Skip to content

Commit c94cad5

Browse files
author
Yevhen Miroshnychenko
committed
MAGETWO-94241: Mysql reconnect does not work
1 parent 911a6e6 commit c94cad5

File tree

3 files changed

+187
-39
lines changed

3 files changed

+187
-39
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: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,9 @@ public function _executeWithBinding(array $params)
6161
$statement->bindParam($paramName, $bindValues[$name], $dataType, $length, $driverOptions);
6262
}
6363

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

7169
/**
@@ -88,9 +86,31 @@ public function _execute(array $params = null)
8886
}
8987

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

0 commit comments

Comments
 (0)