3 namespace Drupal\KernelTests\Core\Database;
5 use Drupal\Core\Database\Database;
6 use Drupal\Core\Database\DatabaseExceptionWrapper;
9 * Tests of the core database system.
13 class ConnectionTest extends DatabaseTestBase {
16 * Tests that connections return appropriate connection objects.
18 public function testConnectionRouting() {
19 // Clone the primary credentials to a replica connection.
20 // Note this will result in two independent connection objects that happen
21 // to point to the same place.
22 $connection_info = Database::getConnectionInfo('default');
23 Database::addConnectionInfo('default', 'replica', $connection_info['default']);
25 $db1 = Database::getConnection('default', 'default');
26 $db2 = Database::getConnection('replica', 'default');
28 $this->assertNotNull($db1, 'default connection is a real connection object.');
29 $this->assertNotNull($db2, 'replica connection is a real connection object.');
30 $this->assertNotIdentical($db1, $db2, 'Each target refers to a different connection.');
32 // Try to open those targets another time, that should return the same objects.
33 $db1b = Database::getConnection('default', 'default');
34 $db2b = Database::getConnection('replica', 'default');
35 $this->assertSame($db1, $db1b, 'A second call to getConnection() returns the same object.');
36 $this->assertSame($db2, $db2b, 'A second call to getConnection() returns the same object.');
38 // Try to open an unknown target.
39 $unknown_target = $this->randomMachineName();
40 $db3 = Database::getConnection($unknown_target, 'default');
41 $this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.');
42 $this->assertSame($db1, $db3, 'An unknown target opens the default connection.');
44 // Try to open that unknown target another time, that should return the same object.
45 $db3b = Database::getConnection($unknown_target, 'default');
46 $this->assertSame($db3, $db3b, 'A second call to getConnection() returns the same object.');
50 * Tests that connections return appropriate connection objects.
52 public function testConnectionRoutingOverride() {
53 // Clone the primary credentials to a replica connection.
54 // Note this will result in two independent connection objects that happen
55 // to point to the same place.
56 $connection_info = Database::getConnectionInfo('default');
57 Database::addConnectionInfo('default', 'replica', $connection_info['default']);
59 Database::ignoreTarget('default', 'replica');
61 $db1 = Database::getConnection('default', 'default');
62 $db2 = Database::getConnection('replica', 'default');
64 $this->assertSame($db1, $db2, 'Both targets refer to the same connection.');
68 * Tests the closing of a database connection.
70 public function testConnectionClosing() {
71 // Open the default target so we have an object to compare.
72 $db1 = Database::getConnection('default', 'default');
74 // Try to close the default connection, then open a new one.
75 Database::closeConnection('default', 'default');
76 $db2 = Database::getConnection('default', 'default');
78 // Opening a connection after closing it should yield an object different than the original.
79 $this->assertNotIdentical($db1, $db2, 'Opening the default connection after it is closed returns a new object.');
83 * Tests the connection options of the active database.
85 public function testConnectionOptions() {
86 $connection_info = Database::getConnectionInfo('default');
88 // Be sure we're connected to the default database.
89 $db = Database::getConnection('default', 'default');
90 $connectionOptions = $db->getConnectionOptions();
92 // In the MySQL driver, the port can be different, so check individual
94 $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], 'The default connection info driver matches the current connection options driver.');
95 $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], 'The default connection info database matches the current connection options database.');
97 // Set up identical replica and confirm connection options are identical.
98 Database::addConnectionInfo('default', 'replica', $connection_info['default']);
99 $db2 = Database::getConnection('replica', 'default');
100 // Getting a driver class ensures the namespace option is set.
101 $this->assertEquals($db->getDriverClass('select'), $db2->getDriverClass('select'));
102 $connectionOptions2 = $db2->getConnectionOptions();
104 // Get a fresh copy of the default connection options.
105 $connectionOptions = $db->getConnectionOptions();
106 $this->assertIdentical($connectionOptions, $connectionOptions2, 'The default and replica connection options are identical.');
108 // Set up a new connection with different connection info.
109 $test = $connection_info['default'];
110 $test['database'] .= 'test';
111 Database::addConnectionInfo('test', 'default', $test);
112 $connection_info = Database::getConnectionInfo('test');
114 // Get a fresh copy of the default connection options.
115 $connectionOptions = $db->getConnectionOptions();
116 $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.');
120 * Ensure that you cannot execute multiple statements on phpversion() > 5.5.21 or > 5.6.5.
122 public function testMultipleStatementsForNewPhp() {
123 // This just tests mysql, as other PDO integrations don't allow disabling
124 // multiple statements.
125 if (Database::getConnection()->databaseType() !== 'mysql' || !defined('\PDO::MYSQL_ATTR_MULTI_STATEMENTS')) {
129 $db = Database::getConnection('default', 'default');
130 // Disable the protection at the PHP level.
132 $db->query('SELECT * FROM {test}; SELECT * FROM {test_people}',
134 ['allow_delimiter_in_query' => TRUE]
136 $this->fail('No PDO exception thrown for multiple statements.');
138 catch (DatabaseExceptionWrapper $e) {
139 $this->pass('PDO exception thrown for multiple statements.');
144 * Ensure that you cannot execute multiple statements.
146 public function testMultipleStatements() {
148 $db = Database::getConnection('default', 'default');
150 $db->query('SELECT * FROM {test}; SELECT * FROM {test_people}');
151 $this->fail('No exception thrown for multiple statements.');
153 catch (\InvalidArgumentException $e) {
154 $this->pass('Exception thrown for multiple statements.');
159 * Test the escapeTable(), escapeField() and escapeAlias() methods with all possible reserved words in PostgreSQL.
161 public function testPostgresqlReservedWords() {
162 if (Database::getConnection()->databaseType() !== 'pgsql') {
166 $db = Database::getConnection('default', 'default');
167 $stmt = $db->query("SELECT word FROM pg_get_keywords() WHERE catcode IN ('R', 'T')");
169 foreach ($stmt->fetchAllAssoc('word') as $word => $row) {
170 $expected = '"' . $word . '"';
171 $this->assertIdentical($db->escapeTable($word), $expected, format_string('The reserved word %word was correctly escaped when used as a table name.', ['%word' => $word]));
172 $this->assertIdentical($db->escapeField($word), $expected, format_string('The reserved word %word was correctly escaped when used as a column name.', ['%word' => $word]));
173 $this->assertIdentical($db->escapeAlias($word), $expected, format_string('The reserved word %word was correctly escaped when used as an alias.', ['%word' => $word]));