3 namespace Drupal\KernelTests\Core\Database;
5 use Drupal\Core\Database\Database;
6 use Drupal\KernelTests\KernelTestBase;
9 * Tests management of database connections.
13 class ConnectionUnitTest extends KernelTestBase {
19 protected $originalCount;
21 protected function setUp() {
24 $this->key = 'default';
25 $this->originalTarget = 'default';
26 $this->target = 'DatabaseConnectionUnitTest';
28 // Determine whether the database driver is MySQL. If it is not, the test
29 // methods will not be executed.
30 // @todo Make this test driver-agnostic, or find a proper way to skip it.
31 // See https://www.drupal.org/node/1273478.
32 $connection_info = Database::getConnectionInfo('default');
33 $this->skipTest = (bool) ($connection_info['default']['driver'] != 'mysql');
34 if ($this->skipTest) {
35 // Insert an assertion to prevent Simpletest from interpreting the test
37 $this->pass('This test is only compatible with MySQL.');
40 // Create an additional connection to monitor the connections being opened
41 // and closed in this test.
42 // @see TestBase::changeDatabasePrefix()
43 Database::addConnectionInfo('default', 'monitor', $connection_info['default']);
44 $this->monitor = Database::getConnection('monitor');
48 * Adds a new database connection info to Database.
50 protected function addConnection() {
51 // Add a new target to the connection, by cloning the current connection.
52 $connection_info = Database::getConnectionInfo($this->key);
53 Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]);
55 // Verify that the new target exists.
56 $info = Database::getConnectionInfo($this->key);
57 // Note: Custom assertion message to not expose database credentials.
58 $this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.');
62 * Returns the connection ID of the current test connection.
66 protected function getConnectionID() {
67 return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField();
71 * Asserts that a connection ID exists.
74 * The connection ID to verify.
76 protected function assertConnection($id) {
77 $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
78 return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', ['@id' => $id]));
82 * Asserts that a connection ID does not exist.
85 * The connection ID to verify.
87 protected function assertNoConnection($id) {
88 $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
89 return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', ['@id' => $id]));
93 * Tests Database::closeConnection() without query.
95 * @todo getConnectionID() executes a query.
97 public function testOpenClose() {
98 if ($this->skipTest) {
101 // Add and open a new connection.
102 $this->addConnection();
103 $id = $this->getConnectionID();
104 Database::getConnection($this->target, $this->key);
106 // Verify that there is a new connection.
107 $this->assertConnection($id);
109 // Close the connection.
110 Database::closeConnection($this->target, $this->key);
111 // Wait 20ms to give the database engine sufficient time to react.
114 // Verify that we are back to the original connection count.
115 $this->assertNoConnection($id);
119 * Tests Database::closeConnection() with a query.
121 public function testOpenQueryClose() {
122 if ($this->skipTest) {
125 // Add and open a new connection.
126 $this->addConnection();
127 $id = $this->getConnectionID();
128 Database::getConnection($this->target, $this->key);
130 // Verify that there is a new connection.
131 $this->assertConnection($id);
134 Database::getConnection($this->target, $this->key)->query('SHOW TABLES');
136 // Close the connection.
137 Database::closeConnection($this->target, $this->key);
138 // Wait 20ms to give the database engine sufficient time to react.
141 // Verify that we are back to the original connection count.
142 $this->assertNoConnection($id);
146 * Tests Database::closeConnection() with a query and custom prefetch method.
148 public function testOpenQueryPrefetchClose() {
149 if ($this->skipTest) {
152 // Add and open a new connection.
153 $this->addConnection();
154 $id = $this->getConnectionID();
155 Database::getConnection($this->target, $this->key);
157 // Verify that there is a new connection.
158 $this->assertConnection($id);
161 Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol();
163 // Close the connection.
164 Database::closeConnection($this->target, $this->key);
165 // Wait 20ms to give the database engine sufficient time to react.
168 // Verify that we are back to the original connection count.
169 $this->assertNoConnection($id);
173 * Tests Database::closeConnection() with a select query.
175 public function testOpenSelectQueryClose() {
176 if ($this->skipTest) {
179 // Add and open a new connection.
180 $this->addConnection();
181 $id = $this->getConnectionID();
182 Database::getConnection($this->target, $this->key);
184 // Verify that there is a new connection.
185 $this->assertConnection($id);
189 Database::getConnection($this->target, $this->key)->schema()->createTable($name, [
199 Database::getConnection($this->target, $this->key)->select('foo', 'f')
200 ->fields('f', ['name'])
205 Database::getConnection($this->target, $this->key)->schema()->dropTable($name);
207 // Close the connection.
208 Database::closeConnection($this->target, $this->key);
209 // Wait 20ms to give the database engine sufficient time to react.
212 // Verify that we are back to the original connection count.
213 $this->assertNoConnection($id);
217 * Tests pdo options override.
219 public function testConnectionOpen() {
220 $connection = Database::getConnection('default');
221 $reflection = new \ReflectionObject($connection);
222 $connection_property = $reflection->getProperty('connection');
223 $connection_property->setAccessible(TRUE);
224 // Skip this test when a database driver does not implement PDO.
225 // An alternative database driver that does not implement PDO
226 // should implement its own connection test.
227 if (get_class($connection_property->getValue($connection)) !== 'PDO') {
228 $this->markTestSkipped('Ignored PDO connection unit test for this driver because it does not implement PDO.');
230 $error_mode = $connection_property->getValue($connection)
231 ->getAttribute(\PDO::ATTR_ERRMODE);
232 $this->assertEqual($error_mode, \PDO::ERRMODE_EXCEPTION, 'Ensure the default error mode is set to exception.');
234 $connection = Database::getConnectionInfo('default');
235 $connection['default']['pdo'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_SILENT;
236 Database::addConnectionInfo('test', 'default', $connection['default']);
237 $connection = Database::getConnection('default', 'test');
239 $reflection = new \ReflectionObject($connection);
240 $connection_property = $reflection->getProperty('connection');
241 $connection_property->setAccessible(TRUE);
242 $error_mode = $connection_property->getValue($connection)
243 ->getAttribute(\PDO::ATTR_ERRMODE);
244 $this->assertEqual($error_mode, \PDO::ERRMODE_SILENT, 'Ensure PDO connection options can be overridden.');
246 Database::removeConnection('test');