Failed Conditions
Pull Request — master (#3074)
by Sergei
03:26
created

testLastInsertIdReusePreparedStatementQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\DBAL\Functional;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Schema\Sequence;
7
use Doctrine\DBAL\Schema\Table;
8
use Doctrine\Tests\DbalFunctionalTestCase;
9
use Doctrine\Tests\TestUtil;
10
11
class LastInsertIdTest extends DbalFunctionalTestCase
12
{
13
    /** @var Connection */
14
    private $testConnection;
15
16
    protected function setUp() : void
17
    {
18
        parent::setUp();
19
20
        $this->testConnection = TestUtil::getConnection();
21
22
        $this->createTable('last_insert_id_table');
23
    }
24
25
    protected function tearDown() : void
26
    {
27
        $this->testConnection->close();
28
29
        if ($this->_conn->getDatabasePlatform()->getName() !== 'sqlite') {
30
            $this->_conn->getSchemaManager()->dropTable('last_insert_id_table');
31
        }
32
33
        parent::tearDown();
34
    }
35
36
    private function createTable(string $tableName) : void
37
    {
38
        $table = new Table($tableName);
39
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
40
        $table->addColumn('foo', 'integer', ['notnull' => false]);
41
        $table->setPrimaryKey(['id']);
42
43
        $connection = $this->_conn->getDatabasePlatform()->getName() === 'sqlite'
44
            ? $this->testConnection
45
            : $this->_conn;
46
47
        $connection->getSchemaManager()->createTable($table);
48
    }
49
50
    public function testLastInsertIdNoInsert() : void
51
    {
52
        $this->assertSame(0, $this->testConnection->lastInsertId());
53
    }
54
55
    public function testLastInsertIdExec() : void
56
    {
57
        $this->assertLastInsertId($this->createExecInsertExecutor());
58
    }
59
60
    public function testLastInsertIdPrepare() : void
61
    {
62
        $this->assertLastInsertId($this->createPrepareInsertExecutor());
63
    }
64
65
    public function testLastInsertIdQuery() : void
66
    {
67
        $this->assertLastInsertId($this->createQueryInsertExecutor());
68
    }
69
70
    private function assertLastInsertId(callable $insertExecutor) : void
71
    {
72
        $this->ensurePlatformSupportsIdentityColumns();
73
74
        $insertExecutor();
75
76
        $this->assertSame(1, $this->testConnection->lastInsertId());
77
    }
78
79
    public function testLastInsertIdAfterUpdateExec() : void
80
    {
81
        $this->assertLastInsertIdAfterUpdate($this->createExecInsertExecutor());
82
    }
83
84
    public function testLastInsertIdAfterUpdatePrepare() : void
85
    {
86
        $this->assertLastInsertIdAfterUpdate($this->createPrepareInsertExecutor());
87
    }
88
89
    public function testLastInsertIdAfterUpdateQuery() : void
90
    {
91
        $this->assertLastInsertIdAfterUpdate($this->createQueryInsertExecutor());
92
    }
93
94
    private function assertLastInsertIdAfterUpdate(callable $insertExecutor) : void
95
    {
96
        $this->ensurePlatformSupportsIdentityColumns();
97
98
        $insertExecutor();
99
        $this->testConnection->update('last_insert_id_table', ['foo' => 2], ['id' => 1]);
100
101
        $this->assertSame(1, $this->testConnection->lastInsertId());
102
    }
103
104
    public function testLastInsertIdAfterDeleteExec() : void
105
    {
106
        $this->assertLastInsertIdAfterDelete($this->createExecInsertExecutor());
107
    }
108
109
    public function testLastInsertIdAfterDeletePrepare() : void
110
    {
111
        $this->assertLastInsertIdAfterDelete($this->createPrepareInsertExecutor());
112
    }
113
114
    public function testLastInsertIdAfterDeleteQuery() : void
115
    {
116
        $this->assertLastInsertIdAfterDelete($this->createQueryInsertExecutor());
117
    }
118
119
    private function assertLastInsertIdAfterDelete(callable $insertExecutor) : void
120
    {
121
        $this->ensurePlatformSupportsIdentityColumns();
122
123
        $insertExecutor();
124
        $this->testConnection->exec('DELETE FROM last_insert_id_table');
125
126
        $this->assertSame(1, $this->testConnection->lastInsertId());
127
    }
128
129
    public function testLastInsertIdAfterTruncateExec() : void
130
    {
131
        $this->assertLastInsertIdAfterTruncate($this->createExecInsertExecutor());
132
    }
133
134
    public function testLastInsertIdAfterTruncatePrepare() : void
135
    {
136
        $this->assertLastInsertIdAfterTruncate($this->createPrepareInsertExecutor());
137
    }
138
139
    public function testLastInsertIdAfterTruncateQuery() : void
140
    {
141
        $this->assertLastInsertIdAfterTruncate($this->createQueryInsertExecutor());
142
    }
143
144
    private function assertLastInsertIdAfterTruncate(callable $insertExecutor) : void
145
    {
146
        $this->ensurePlatformSupportsIdentityColumns();
147
148
        $insertExecutor();
149
        $truncateTableSql = $this->testConnection->getDatabasePlatform()->getTruncateTableSQL('last_insert_id_table');
150
        $this->testConnection->exec($truncateTableSql);
151
152
        $this->assertSame(1, $this->testConnection->lastInsertId());
153
    }
154
155
    public function testLastInsertIdAfterDropTableExec() : void
156
    {
157
        $this->assertLastInsertIdAfterDropTable($this->createExecInsertExecutor());
158
    }
159
160
    public function testLastInsertIdAfterDropTablePrepare() : void
161
    {
162
        $this->assertLastInsertIdAfterDropTable($this->createPrepareInsertExecutor());
163
    }
164
165
    public function testLastInsertIdAfterDropTableQuery() : void
166
    {
167
        $this->assertLastInsertIdAfterDropTable($this->createQueryInsertExecutor());
168
    }
169
170
    private function assertLastInsertIdAfterDropTable(callable $insertExecutor) : void
171
    {
172
        $this->ensurePlatformSupportsIdentityColumns();
173
174
        $this->createTable('last_insert_id_table_tmp');
175
176
        $insertExecutor();
177
        $this->testConnection->getSchemaManager()->dropTable('last_insert_id_table_tmp');
178
179
        $this->assertSame(1, $this->testConnection->lastInsertId());
180
    }
181
182
    public function testLastInsertIdAfterSelectExec() : void
183
    {
184
        $this->assertLastInsertIdAfterSelect($this->createExecInsertExecutor());
185
    }
186
187
    public function testLastInsertIdAfterSelectPrepare() : void
188
    {
189
        $this->assertLastInsertIdAfterSelect($this->createPrepareInsertExecutor());
190
    }
191
192
    public function testLastInsertIdAfterSelectQuery() : void
193
    {
194
        $this->assertLastInsertIdAfterSelect($this->createQueryInsertExecutor());
195
    }
196
197
    private function assertLastInsertIdAfterSelect(callable $insertExecutor) : void
198
    {
199
        $this->ensurePlatformSupportsIdentityColumns();
200
201
        $insertExecutor();
202
        $this->testConnection->executeQuery('SELECT 1 FROM last_insert_id_table');
203
204
        $this->assertSame(1, $this->testConnection->lastInsertId());
205
    }
206
207
    public function testLastInsertIdInTransactionExec() : void
208
    {
209
        $this->assertLastInsertIdInTransaction($this->createExecInsertExecutor());
210
    }
211
212
    public function testLastInsertIdInTransactionPrepare() : void
213
    {
214
        $this->assertLastInsertIdInTransaction($this->createPrepareInsertExecutor());
215
    }
216
217
    public function testLastInsertIdInTransactionQuery() : void
218
    {
219
        $this->assertLastInsertIdInTransaction($this->createQueryInsertExecutor());
220
    }
221
222
    public function assertLastInsertIdInTransaction(callable $insertExecutor) : void
223
    {
224
        $this->ensurePlatformSupportsIdentityColumns();
225
226
        $this->testConnection->beginTransaction();
227
        $insertExecutor();
228
        $this->assertSame(1, $this->testConnection->lastInsertId());
229
        $this->testConnection->rollBack();
230
    }
231
232
    public function testLastInsertIdAfterTransactionCommitExec() : void
233
    {
234
        $this->assertLastInsertIdAfterTransactionCommit($this->createExecInsertExecutor());
235
    }
236
237
    public function testLastInsertIdAfterTransactionCommitPrepare() : void
238
    {
239
        $this->assertLastInsertIdAfterTransactionCommit($this->createPrepareInsertExecutor());
240
    }
241
242
    public function testLastInsertIdAfterTransactionCommitQuery() : void
243
    {
244
        $this->assertLastInsertIdAfterTransactionCommit($this->createQueryInsertExecutor());
245
    }
246
247
    private function assertLastInsertIdAfterTransactionCommit(callable $insertExecutor) : void
248
    {
249
        $this->ensurePlatformSupportsIdentityColumns();
250
251
        $this->testConnection->beginTransaction();
252
        $insertExecutor();
253
        $this->testConnection->commit();
254
255
        $this->assertSame(1, $this->testConnection->lastInsertId());
256
    }
257
258
    public function testLastInsertIdAfterTransactionRollbackExec() : void
259
    {
260
        $this->assertLastInsertIdAfterTransactionRollback($this->createExecInsertExecutor());
261
    }
262
263
    public function testLastInsertIdAfterTransactionRollbackPrepare() : void
264
    {
265
        $this->assertLastInsertIdAfterTransactionRollback($this->createPrepareInsertExecutor());
266
    }
267
268
    public function testLastInsertIdAfterTransactionRollbackQuery() : void
269
    {
270
        $this->assertLastInsertIdAfterTransactionRollback($this->createQueryInsertExecutor());
271
    }
272
273
    private function assertLastInsertIdAfterTransactionRollback(callable $insertExecutor) : void
274
    {
275
        $this->ensurePlatformSupportsIdentityColumns();
276
277
        $this->testConnection->beginTransaction();
278
        $insertExecutor();
279
        $this->testConnection->rollBack();
280
281
        $this->assertSame(1, $this->testConnection->lastInsertId());
282
    }
283
284
    public function testLastInsertIdInsertAfterTransactionRollbackExec() : void
285
    {
286
        $this->assertLastInsertIdInsertAfterTransactionRollback($this->createExecInsertExecutor());
287
    }
288
289
    public function testLastInsertIdInsertAfterTransactionRollbackPrepare() : void
290
    {
291
        $this->assertLastInsertIdInsertAfterTransactionRollback($this->createPrepareInsertExecutor());
292
    }
293
294
    public function testLastInsertIdInsertAfterTransactionRollbackQuery() : void
295
    {
296
        $this->assertLastInsertIdInsertAfterTransactionRollback($this->createQueryInsertExecutor());
297
    }
298
299
    private function assertLastInsertIdInsertAfterTransactionRollback(callable $insertExecutor) : void
300
    {
301
        $this->ensurePlatformSupportsIdentityColumns();
302
303
        $this->testConnection->beginTransaction();
304
        $insertExecutor();
305
        $this->testConnection->rollBack();
306
        $insertExecutor();
307
308
        $expected = $this->testConnection->getDatabasePlatform()->getName() === 'sqlite'
309
            // SQLite has a different transaction concept, that reuses rolled back IDs
310
            // See: http://sqlite.1065341.n5.nabble.com/Autoincrement-with-rollback-td79154.html
311
            ? 1
312
            : 2;
313
314
        $this->assertSame($expected, $this->testConnection->lastInsertId());
315
    }
316
317
    public function testLastInsertIdReusePreparedStatementPrepare() : void
318
    {
319
        $this->ensurePlatformSupportsIdentityColumns();
320
321
        $statement = $this->testConnection->prepare('INSERT INTO last_insert_id_table (foo) VALUES (1)');
322
323
        $statement->execute();
324
        $statement->execute();
325
326
        $this->assertSame(2, $this->testConnection->lastInsertId());
327
    }
328
329
    public function testLastInsertIdReusePreparedStatementQuery() : void
330
    {
331
        $this->ensurePlatformSupportsIdentityColumns();
332
333
        $statement = $this->testConnection->query('INSERT INTO last_insert_id_table (foo) VALUES (1)');
334
335
        $statement->execute();
336
337
        $this->assertSame(2, $this->testConnection->lastInsertId());
338
    }
339
340
    public function testLastInsertIdConnectionScope() : void
341
    {
342
        $platform = $this->_conn->getDatabasePlatform();
343
344
        if ($platform->getName() === 'sqlite') {
345
            $this->markTestSkipped('Test does not work on sqlite as connections do not share memory.');
346
        }
347
348
        $this->ensurePlatformSupportsIdentityColumns();
349
350
        $connection1 = TestUtil::getConnection();
351
        $connection2 = TestUtil::getConnection();
352
353
        $connection1->insert('last_insert_id_table', ['foo' => 1]);
354
355
        $this->assertNotSame(1, $connection2->lastInsertId());
356
357
        $connection2->insert('last_insert_id_table', ['foo' => 2]);
358
359
        $this->assertSame(1, $connection1->lastInsertId());
360
        $this->assertSame(2, $connection2->lastInsertId());
361
362
        $connection1->close();
363
        $connection2->close();
364
    }
365
366
    public function testLastInsertIdSequence() : void
367
    {
368
        if (! $this->_conn->getDatabasePlatform()->supportsSequences()) {
369
            $this->markTestSkipped('Test only works on platforms with sequences.');
370
        }
371
372
        $sequence = new Sequence('last_insert_id_seq');
373
374
        $this->_conn->getSchemaManager()->createSequence($sequence);
375
376
        $nextSequenceValueSql = $this->_conn->getDatabasePlatform()->getSequenceNextValSQL('last_insert_id_seq');
377
        $nextSequenceValue    = $this->_conn->fetchColumn($nextSequenceValueSql);
378
        $lastInsertId         = $this->_conn->lastInsertId('last_insert_id_seq');
379
380
        $this->assertEquals($lastInsertId, $nextSequenceValue);
381
    }
382
383
    public function testLastInsertIdSequenceEmulatedIdentityColumnExec() : void
384
    {
385
        $this->assertLastInsertIdSequenceEmulatedIdentityColumn($this->createExecInsertExecutor());
386
    }
387
388
    public function testLastInsertIdSequenceEmulatedIdentityColumnPrepare() : void
389
    {
390
        $this->assertLastInsertIdSequenceEmulatedIdentityColumn($this->createPrepareInsertExecutor());
391
    }
392
393
    public function testLastInsertIdSequenceEmulatedIdentityColumnQuery() : void
394
    {
395
        $this->assertLastInsertIdSequenceEmulatedIdentityColumn($this->createQueryInsertExecutor());
396
    }
397
398
    private function ensurePlatformSupportsIdentityColumns() : void
399
    {
400
        if ($this->_conn->getDatabasePlatform()->supportsIdentityColumns()) {
401
            return;
402
        }
403
404
        $this->markTestSkipped('Test only works on platforms with identity columns.');
405
    }
406
407
    private function assertLastInsertIdSequenceEmulatedIdentityColumn(callable $insertExecutor) : void
408
    {
409
        $platform = $this->_conn->getDatabasePlatform();
410
411
        if ($platform->supportsIdentityColumns() || ! $platform->usesSequenceEmulatedIdentityColumns()) {
412
            $this->markTestSkipped('Test only works on platforms that emulates identity columns through sequences.');
413
        }
414
415
        $sequenceName = $platform->getIdentitySequenceName('last_insert_id_table', 'id');
416
417
        $this->assertSame(0, $this->_conn->lastInsertId($sequenceName));
418
419
        $insertExecutor();
420
421
        $this->assertSame(1, $this->_conn->lastInsertId($sequenceName));
422
    }
423
424
    private function createExecInsertExecutor() : callable
425
    {
426
        return function () {
427
            $this->testConnection->getWrappedConnection()->exec('INSERT INTO last_insert_id_table (foo) VALUES (1)');
428
        };
429
    }
430
431
    private function createPrepareInsertExecutor() : callable
432
    {
433
        return function () {
434
            $stmt = $this->testConnection->getWrappedConnection()->prepare(
435
                'INSERT INTO last_insert_id_table (foo) VALUES (?)'
436
            );
437
438
            $stmt->execute([1]);
439
        };
440
    }
441
442
    private function createQueryInsertExecutor() : callable
443
    {
444
        return function () {
445
            $this->testConnection->getWrappedConnection()->query('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

445
            $this->testConnection->getWrappedConnection()->/** @scrutinizer ignore-call */ query('INSERT INTO last_insert_id_table (foo) VALUES (1)');

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...
446
        };
447
    }
448
}
449