Failed Conditions
Push — master ( ea4232...3b6e69 )
by Grégoire
17:30 queued 17:24
created

tests/Functional/MasterSlaveConnectionTest.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Tests\Functional;
6
7
use Doctrine\DBAL\Connections\MasterSlaveConnection;
8
use Doctrine\DBAL\Driver\Statement;
9
use Doctrine\DBAL\DriverManager;
10
use Doctrine\DBAL\Schema\Table;
11
use Doctrine\DBAL\Tests\FunctionalTestCase;
12
use Throwable;
13
use function array_change_key_case;
14
use function assert;
15
use function sprintf;
16
use function strlen;
17
use function strtolower;
18
use function substr;
19
use const CASE_LOWER;
20
21
/**
22
 * @group DBAL-20
23
 */
24
class MasterSlaveConnectionTest extends FunctionalTestCase
25
{
26
    protected function setUp() : void
27
    {
28
        parent::setUp();
29
30
        $platformName = $this->connection->getDatabasePlatform()->getName();
31
32
        // This is a MySQL specific test, skip other vendors.
33
        if ($platformName !== 'mysql') {
34
            self::markTestSkipped(sprintf('Test does not work on %s.', $platformName));
35
        }
36
37
        try {
38
            $table = new Table('master_slave_table');
39
            $table->addColumn('test_int', 'integer');
40
            $table->setPrimaryKey(['test_int']);
41
42
            $sm = $this->connection->getSchemaManager();
43
            $sm->createTable($table);
44
        } catch (Throwable $e) {
45
        }
46
47
        $this->connection->executeUpdate('DELETE FROM master_slave_table');
48
        $this->connection->insert('master_slave_table', ['test_int' => 1]);
49
    }
50
51
    private function createMasterSlaveConnection(bool $keepSlave = false) : MasterSlaveConnection
52
    {
53
        $connection = DriverManager::getConnection($this->createMasterSlaveConnectionParams($keepSlave));
54
        assert($connection instanceof MasterSlaveConnection);
55
56
        return $connection;
57
    }
58
59
    /**
60
     * @return mixed[]
61
     */
62
    private function createMasterSlaveConnectionParams(bool $keepSlave = false) : array
63
    {
64
        $params                 = $this->connection->getParams();
65
        $params['master']       = $params;
66
        $params['slaves']       = [$params, $params];
67
        $params['keepSlave']    = $keepSlave;
68
        $params['wrapperClass'] = MasterSlaveConnection::class;
69
70
        return $params;
71
    }
72
73
    public function testInheritCharsetFromMaster() : void
74
    {
75
        $charsets = [
76
            'utf8',
77
            'latin1',
78
        ];
79
80
        foreach ($charsets as $charset) {
81
            $params                      = $this->createMasterSlaveConnectionParams();
82
            $params['master']['charset'] = $charset;
83
84
            foreach ($params['slaves'] as $index => $slaveParams) {
85
                if (! isset($slaveParams['charset'])) {
86
                    continue;
87
                }
88
89
                unset($params['slaves'][$index]['charset']);
90
            }
91
92
            $conn = DriverManager::getConnection($params);
93
            self::assertInstanceOf(MasterSlaveConnection::class, $conn);
94
            $conn->connect('slave');
0 ignored issues
show
The call to Doctrine\DBAL\Connection::connect() has too many arguments starting with 'slave'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
            $conn->/** @scrutinizer ignore-call */ 
95
                   connect('slave');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
95
96
            self::assertFalse($conn->isConnectedToMaster());
0 ignored issues
show
The method isConnectedToMaster() does not exist on Doctrine\DBAL\Connection. Did you maybe mean isConnected()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

96
            self::assertFalse($conn->/** @scrutinizer ignore-call */ isConnectedToMaster());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
97
98
            $clientCharset = $conn->fetchColumn('select @@character_set_client as c');
99
100
            self::assertSame(
101
                $charset,
102
                substr(strtolower($clientCharset), 0, strlen($charset))
103
            );
104
        }
105
    }
106
107
    public function testMasterOnConnect() : void
108
    {
109
        $conn = $this->createMasterSlaveConnection();
110
111
        self::assertFalse($conn->isConnectedToMaster());
112
        $conn->connect('slave');
113
        self::assertFalse($conn->isConnectedToMaster());
114
        $conn->connect('master');
115
        self::assertTrue($conn->isConnectedToMaster());
116
    }
117
118
    public function testNoMasterOnExecuteQuery() : void
119
    {
120
        $conn = $this->createMasterSlaveConnection();
121
122
        $sql     = 'SELECT count(*) as num FROM master_slave_table';
123
        $data    = $conn->fetchAll($sql);
124
        $data[0] = array_change_key_case($data[0], CASE_LOWER);
125
126
        self::assertEquals(1, $data[0]['num']);
127
        self::assertFalse($conn->isConnectedToMaster());
128
    }
129
130
    public function testMasterOnWriteOperation() : void
131
    {
132
        $conn = $this->createMasterSlaveConnection();
133
        $conn->insert('master_slave_table', ['test_int' => 30]);
134
135
        self::assertTrue($conn->isConnectedToMaster());
136
137
        $sql     = 'SELECT count(*) as num FROM master_slave_table';
138
        $data    = $conn->fetchAll($sql);
139
        $data[0] = array_change_key_case($data[0], CASE_LOWER);
140
141
        self::assertEquals(2, $data[0]['num']);
142
        self::assertTrue($conn->isConnectedToMaster());
143
    }
144
145
    /**
146
     * @group DBAL-335
147
     */
148
    public function testKeepSlaveBeginTransactionStaysOnMaster() : void
149
    {
150
        $conn = $this->createMasterSlaveConnection($keepSlave = true);
151
        $conn->connect('slave');
152
153
        $conn->beginTransaction();
154
        $conn->insert('master_slave_table', ['test_int' => 30]);
155
        $conn->commit();
156
157
        self::assertTrue($conn->isConnectedToMaster());
158
159
        $conn->connect();
160
        self::assertTrue($conn->isConnectedToMaster());
161
162
        $conn->connect('slave');
163
        self::assertFalse($conn->isConnectedToMaster());
164
    }
165
166
    /**
167
     * @group DBAL-335
168
     */
169
    public function testKeepSlaveInsertStaysOnMaster() : void
170
    {
171
        $conn = $this->createMasterSlaveConnection($keepSlave = true);
172
        $conn->connect('slave');
173
174
        $conn->insert('master_slave_table', ['test_int' => 30]);
175
176
        self::assertTrue($conn->isConnectedToMaster());
177
178
        $conn->connect();
179
        self::assertTrue($conn->isConnectedToMaster());
180
181
        $conn->connect('slave');
182
        self::assertFalse($conn->isConnectedToMaster());
183
    }
184
185
    public function testMasterSlaveConnectionCloseAndReconnect() : void
186
    {
187
        $conn = $this->createMasterSlaveConnection();
188
        $conn->connect('master');
189
        self::assertTrue($conn->isConnectedToMaster());
190
191
        $conn->close();
192
        self::assertFalse($conn->isConnectedToMaster());
193
194
        $conn->connect('master');
195
        self::assertTrue($conn->isConnectedToMaster());
196
    }
197
198
    public function testQueryOnMaster() : void
199
    {
200
        $conn = $this->createMasterSlaveConnection();
201
202
        $query = 'SELECT count(*) as num FROM master_slave_table';
203
204
        $statement = $conn->query($query);
205
206
        self::assertInstanceOf(Statement::class, $statement);
207
208
        //Query must be executed only on Master
209
        self::assertTrue($conn->isConnectedToMaster());
210
211
        $data = $statement->fetchAll();
212
213
        //Default fetchmode is FetchMode::ASSOCIATIVE
214
        self::assertArrayHasKey(0, $data);
215
        self::assertArrayHasKey('num', $data[0]);
216
217
        //Could be set in other fetchmodes
218
        self::assertArrayNotHasKey(0, $data[0]);
219
        self::assertEquals(1, $data[0]['num']);
220
    }
221
222
    public function testQueryOnSlave() : void
223
    {
224
        $conn = $this->createMasterSlaveConnection();
225
        $conn->connect('slave');
226
227
        $query = 'SELECT count(*) as num FROM master_slave_table';
228
229
        $statement = $conn->query($query);
230
231
        self::assertInstanceOf(Statement::class, $statement);
232
233
        //Query must be executed only on Master, even when we connect to the slave
234
        self::assertTrue($conn->isConnectedToMaster());
235
236
        $data = $statement->fetchAll();
237
238
        //Default fetchmode is FetchMode::ASSOCIATIVE
239
        self::assertArrayHasKey(0, $data);
240
        self::assertArrayHasKey('num', $data[0]);
241
242
        //Could be set in other fetchmodes
243
        self::assertArrayNotHasKey(0, $data[0]);
244
245
        self::assertEquals(1, $data[0]['num']);
246
    }
247
}
248