Failed Conditions
Pull Request — master (#3074)
by Sergei
12:41
created

LastInsertIdTest::executorProvider()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
1
<?php
2
3
namespace Doctrine\Tests\DBAL\Functional;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Driver\Connection as DriverConnection;
7
use Doctrine\DBAL\Schema\Sequence;
8
use Doctrine\DBAL\Schema\Table;
9
use Doctrine\Tests\DbalFunctionalTestCase;
10
use Doctrine\Tests\TestUtil;
11
12
class LastInsertIdTest extends DbalFunctionalTestCase
13
{
14
    /** @var Connection */
15
    private $testConnection;
16
17
    protected function setUp() : void
18
    {
19
        parent::setUp();
20
21
        $this->testConnection = TestUtil::getConnection();
22
23
        $this->createTable('last_insert_id_table');
24
    }
25
26
    protected function tearDown() : void
27
    {
28
        $this->testConnection->close();
29
30
        if ($this->_conn->getDatabasePlatform()->getName() !== 'sqlite') {
31
            $this->_conn->getSchemaManager()->dropTable('last_insert_id_table');
32
        }
33
34
        parent::tearDown();
35
    }
36
37
    private function createTable(string $tableName) : void
38
    {
39
        $table = new Table($tableName);
40
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
41
        $table->addColumn('foo', 'integer', ['notnull' => false]);
42
        $table->setPrimaryKey(['id']);
43
44
        $connection = $this->_conn->getDatabasePlatform()->getName() === 'sqlite'
45
            ? $this->testConnection
46
            : $this->_conn;
47
48
        $connection->getSchemaManager()->createTable($table);
49
    }
50
51
    public function testLastInsertIdNoInsert() : void
52
    {
53
        $this->assertSame(0, $this->testConnection->lastInsertId());
54
    }
55
56
    /**
57
     * @dataProvider executorProvider
58
     */
59
    public function testLastInsertId(callable $insertExecutor) : void
60
    {
61
        $this->ensurePlatformSupportsIdentityColumns();
62
63
        $insertExecutor($this->testConnection->getWrappedConnection());
64
65
        $this->assertSame(1, $this->testConnection->lastInsertId());
66
    }
67
68
    /**
69
     * @dataProvider executorProvider
70
     */
71
    public function testLastInsertIdAfterUpdate(callable $insertExecutor) : void
72
    {
73
        $this->ensurePlatformSupportsIdentityColumns();
74
75
        $insertExecutor($this->testConnection->getWrappedConnection());
76
        $this->testConnection->update('last_insert_id_table', ['foo' => 2], ['id' => 1]);
77
78
        $this->assertSame(1, $this->testConnection->lastInsertId());
79
    }
80
81
    /**
82
     * @dataProvider executorProvider
83
     */
84
    public function testLastInsertIdAfterDelete(callable $insertExecutor) : void
85
    {
86
        $this->ensurePlatformSupportsIdentityColumns();
87
88
        $insertExecutor($this->testConnection->getWrappedConnection());
89
        $this->testConnection->exec('DELETE FROM last_insert_id_table');
90
91
        $this->assertSame(1, $this->testConnection->lastInsertId());
92
    }
93
94
    /**
95
     * @dataProvider executorProvider
96
     */
97
    public function testLastInsertIdAfterTruncate(callable $insertExecutor) : void
98
    {
99
        $this->ensurePlatformSupportsIdentityColumns();
100
101
        $insertExecutor($this->testConnection->getWrappedConnection());
102
        $truncateTableSql = $this->testConnection->getDatabasePlatform()->getTruncateTableSQL('last_insert_id_table');
103
        $this->testConnection->exec($truncateTableSql);
104
105
        $this->assertSame(1, $this->testConnection->lastInsertId());
106
    }
107
108
    /**
109
     * @dataProvider executorProvider
110
     */
111
    public function testLastInsertIdAfterDropTable(callable $insertExecutor) : void
112
    {
113
        $this->ensurePlatformSupportsIdentityColumns();
114
115
        $this->createTable('last_insert_id_table_tmp');
116
117
        $insertExecutor($this->testConnection->getWrappedConnection());
118
        $this->testConnection->getSchemaManager()->dropTable('last_insert_id_table_tmp');
119
120
        $this->assertSame(1, $this->testConnection->lastInsertId());
121
    }
122
123
    /**
124
     * @dataProvider executorProvider
125
     */
126
    public function testLastInsertIdAfterSelect(callable $insertExecutor) : void
127
    {
128
        $this->ensurePlatformSupportsIdentityColumns();
129
130
        $insertExecutor($this->testConnection->getWrappedConnection());
131
        $this->testConnection->executeQuery('SELECT 1 FROM last_insert_id_table');
132
133
        $this->assertSame(1, $this->testConnection->lastInsertId());
134
    }
135
136
    /**
137
     * @dataProvider executorProvider
138
     */
139
    public function testLastInsertIdInTransaction(callable $insertExecutor) : void
140
    {
141
        $this->ensurePlatformSupportsIdentityColumns();
142
143
        $this->testConnection->beginTransaction();
144
        $insertExecutor($this->testConnection->getWrappedConnection());
145
        $this->assertSame(1, $this->testConnection->lastInsertId());
146
        $this->testConnection->rollBack();
147
    }
148
149
    /**
150
     * @dataProvider executorProvider
151
     */
152
    public function testLastInsertIdAfterTransactionCommit(callable $insertExecutor) : void
153
    {
154
        $this->ensurePlatformSupportsIdentityColumns();
155
156
        $this->testConnection->beginTransaction();
157
        $insertExecutor($this->testConnection->getWrappedConnection());
158
        $this->testConnection->commit();
159
160
        $this->assertSame(1, $this->testConnection->lastInsertId());
161
    }
162
163
    /**
164
     * @dataProvider executorProvider
165
     */
166
    public function testLastInsertIdAfterTransactionRollback(callable $insertExecutor) : void
167
    {
168
        $this->ensurePlatformSupportsIdentityColumns();
169
170
        $this->testConnection->beginTransaction();
171
        $insertExecutor($this->testConnection->getWrappedConnection());
172
        $this->testConnection->rollBack();
173
174
        $this->assertSame(1, $this->testConnection->lastInsertId());
175
    }
176
177
    /**
178
     * @dataProvider executorProvider
179
     */
180
    public function testLastInsertIdInsertAfterTransactionRollback(callable $insertExecutor) : void
181
    {
182
        $this->ensurePlatformSupportsIdentityColumns();
183
184
        $this->testConnection->beginTransaction();
185
        $insertExecutor($this->testConnection->getWrappedConnection());
186
        $this->testConnection->rollBack();
187
        $insertExecutor($this->testConnection->getWrappedConnection());
188
189
        $expected = $this->testConnection->getDatabasePlatform()->getName() === 'sqlite'
190
            // SQLite has a different transaction concept, that reuses rolled back IDs
191
            // See: http://sqlite.1065341.n5.nabble.com/Autoincrement-with-rollback-td79154.html
192
            ? 1
193
            : 2;
194
195
        $this->assertSame($expected, $this->testConnection->lastInsertId());
196
    }
197
198
    public function testLastInsertIdReusePreparedStatementPrepare() : void
199
    {
200
        $this->ensurePlatformSupportsIdentityColumns();
201
202
        $statement = $this->testConnection->prepare('INSERT INTO last_insert_id_table (foo) VALUES (1)');
203
204
        $statement->execute();
205
        $statement->execute();
206
207
        $this->assertSame(2, $this->testConnection->lastInsertId());
208
    }
209
210
    public function testLastInsertIdReusePreparedStatementQuery() : void
211
    {
212
        $this->ensurePlatformSupportsIdentityColumns();
213
214
        $statement = $this->testConnection->query('INSERT INTO last_insert_id_table (foo) VALUES (1)');
215
216
        $statement->execute();
217
218
        $this->assertSame(2, $this->testConnection->lastInsertId());
219
    }
220
221
    public function testLastInsertIdConnectionScope() : void
222
    {
223
        $platform = $this->_conn->getDatabasePlatform();
224
225
        if ($platform->getName() === 'sqlite') {
226
            $this->markTestSkipped('Test does not work on sqlite as connections do not share memory.');
227
        }
228
229
        $this->ensurePlatformSupportsIdentityColumns();
230
231
        $connection1 = TestUtil::getConnection();
232
        $connection2 = TestUtil::getConnection();
233
234
        $connection1->insert('last_insert_id_table', ['foo' => 1]);
235
236
        $this->assertNotSame(1, $connection2->lastInsertId());
237
238
        $connection2->insert('last_insert_id_table', ['foo' => 2]);
239
240
        $this->assertSame(1, $connection1->lastInsertId());
241
        $this->assertSame(2, $connection2->lastInsertId());
242
243
        $connection1->close();
244
        $connection2->close();
245
    }
246
247
    public function testLastInsertIdSequence() : void
248
    {
249
        if (! $this->_conn->getDatabasePlatform()->supportsSequences()) {
250
            $this->markTestSkipped('Test only works on platforms with sequences.');
251
        }
252
253
        $sequence = new Sequence('last_insert_id_seq');
254
255
        $this->_conn->getSchemaManager()->createSequence($sequence);
256
257
        $nextSequenceValueSql = $this->_conn->getDatabasePlatform()->getSequenceNextValSQL('last_insert_id_seq');
258
        $nextSequenceValue    = $this->_conn->fetchColumn($nextSequenceValueSql);
259
        $lastInsertId         = $this->_conn->lastInsertId('last_insert_id_seq');
260
261
        $this->assertEquals($lastInsertId, $nextSequenceValue);
262
    }
263
264
    /**
265
     * @dataProvider executorProvider
266
     */
267
    public function testLastInsertIdSequenceEmulatedIdentityColumn(callable $insertExecutor) : void
268
    {
269
        $platform = $this->_conn->getDatabasePlatform();
270
271
        if ($platform->supportsIdentityColumns() || ! $platform->usesSequenceEmulatedIdentityColumns()) {
272
            $this->markTestSkipped('Test only works on platforms that emulates identity columns through sequences.');
273
        }
274
275
        $sequenceName = $platform->getIdentitySequenceName('last_insert_id_table', 'id');
276
277
        $this->assertSame(0, $this->_conn->lastInsertId($sequenceName));
278
279
        $insertExecutor($this->testConnection->getWrappedConnection());
280
281
        $this->assertSame(1, $this->_conn->lastInsertId($sequenceName));
282
    }
283
284
    private function ensurePlatformSupportsIdentityColumns() : void
285
    {
286
        if ($this->_conn->getDatabasePlatform()->supportsIdentityColumns()) {
287
            return;
288
        }
289
290
        $this->markTestSkipped('Test only works on platforms with identity columns.');
291
    }
292
293
    public static function executorProvider()
294
    {
295
        foreach (self::getExecutors() as $name => $executor) {
296
            yield $name => [$executor];
297
        }
298
    }
299
300
    private static function getExecutors()
301
    {
302
        return [
303
            'exec' => function (DriverConnection $connection) {
304
                $connection->exec(
305
                    'INSERT INTO last_insert_id_table (foo) VALUES (1)'
306
                );
307
            },
308
            'prepare-insert' => function (DriverConnection $connection) {
309
                $connection->prepare(
310
                    'INSERT INTO last_insert_id_table (foo) VALUES (?)'
311
                )->execute([1]);
312
            },
313
            'query-insert' => function (DriverConnection $connection) {
314
                $connection->query(
315
                    'INSERT INTO last_insert_id_table (foo) VALUES (1)'
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\DBAL\Driver\Connection::query() has too many arguments starting with 'INSERT INTO last_insert...table (foo) VALUES (1)'. ( Ignorable by Annotation )

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

315
                $connection->/** @scrutinizer ignore-call */ 
316
                             query(

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...
316
                );
317
            },
318
        ];
319
    }
320
}
321