Completed
Push — develop ( e7b6c1...8dba78 )
by Marco
22s queued 13s
created

DataAccessTest   F

Complexity

Total Complexity 71

Size/Duplication

Total Lines 949
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 71
eloc 514
dl 0
loc 949
rs 2.7199
c 0
b 0
f 0

50 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 19 2
A testFetchAssocWithTypes() 0 14 1
A testPrepareWithExecuteParams() 0 14 1
A getTrimExpressionData() 0 39 1
A testFetchArrayWithMissingTypes() 0 14 3
A testNativeArrayListSupport() 0 25 2
A testTrimExpressionInvalidMode() 0 4 1
A testFetchArray() 0 7 1
A testPrepareWithFetchAll() 0 16 1
A testFetchAllWithMissingTypes() 0 14 3
A testSqliteDateArithmeticWithDynamicInterval() 0 25 2
A testFetchAll() 0 13 1
A testPrepareWithBindValue() 0 13 1
A testPrepareQueryBindValueDateTimeType() 0 8 1
A testPrepareWithBindParam() 0 16 1
A testFetchNoResult() 0 4 1
A testFetchColumn() 0 11 1
A testTrimExpression() 0 10 1
A testExecuteQueryBindDateTimeType() 0 10 1
A testFetchAllWithTypes() 0 16 1
A testFetchColumnWithTypes() 0 11 1
A testPrepareWithFetchAllBoth() 0 16 1
A testPrepareWithQuoted() 0 13 1
A testFetchBoth() 0 13 1
A testFetchAssoc() 0 11 1
A testDateArithmetics() 0 41 1
A testFetchArrayWithTypes() 0 14 1
A testPrepareWithFetchColumn() 0 15 1
A testFetchColumnWithMissingTypes() 0 14 3
A testFetchAssocWithMissingTypes() 0 14 3
A testPrepareWithIterator() 0 20 2
A testExecuteUpdateBindDateTimeType() 0 21 1
A testLocateExpression() 0 28 1
A testEmptyParameters() 0 7 1
A substringExpressionProvider() 0 20 1
A testQuoteSQLInjection() 0 6 1
A testSetFetchModeClassFetch() 0 20 2
A testSubstringExpression() 0 9 1
A testFetchAllStyleObject() 0 25 4
A testFetchAllSupportFetchClass() 0 20 1
A testFetchColumnNoResult() 0 4 1
A setupFixture() 0 7 1
A testSetDefaultFetchMode() 0 9 1
A testBitComparisonExpressionSupport() 0 45 3
A testSetFetchModeClassFetchAll() 0 17 1
A beforeFetchClassTest() 0 20 4
A testFetchAllStyleColumn() 0 12 1
A testEmptyFetchColumnReturnsFalse() 0 7 1
A testSetFetchModeOnDbalStatement() 0 11 1
A testFetchColumnNullValue() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like DataAccessTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DataAccessTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\Tests\DBAL\Functional;
4
5
use DateTime;
6
use Doctrine\DBAL\Connection;
7
use Doctrine\DBAL\DBALException;
8
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
9
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver;
10
use Doctrine\DBAL\Driver\PDOConnection;
11
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
12
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
13
use Doctrine\DBAL\FetchMode;
14
use Doctrine\DBAL\ParameterType;
15
use Doctrine\DBAL\Platforms\SqlitePlatform;
16
use Doctrine\DBAL\Platforms\TrimMode;
17
use Doctrine\DBAL\Schema\Table;
18
use Doctrine\DBAL\Statement;
19
use Doctrine\DBAL\Types\Type;
20
use Doctrine\Tests\DbalFunctionalTestCase;
21
use InvalidArgumentException;
22
use PDO;
23
use const CASE_LOWER;
24
use const PHP_EOL;
25
use function array_change_key_case;
26
use function array_filter;
27
use function array_keys;
28
use function count;
29
use function date;
30
use function implode;
31
use function is_numeric;
32
use function json_encode;
33
use function property_exists;
34
use function sprintf;
35
use function strtotime;
36
37
class DataAccessTest extends DbalFunctionalTestCase
38
{
39
    /** @var bool */
40
    private static $generated = false;
41
42
    protected function setUp() : void
43
    {
44
        parent::setUp();
45
46
        if (self::$generated !== false) {
47
            return;
48
        }
49
50
        $table = new Table('fetch_table');
51
        $table->addColumn('test_int', 'integer');
52
        $table->addColumn('test_string', 'string');
53
        $table->addColumn('test_datetime', 'datetime', ['notnull' => false]);
54
        $table->setPrimaryKey(['test_int']);
55
56
        $sm = $this->connection->getSchemaManager();
57
        $sm->createTable($table);
58
59
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo', 'test_datetime' => '2010-01-01 10:10:10']);
60
        self::$generated = true;
61
    }
62
63
    public function testPrepareWithBindValue()
64
    {
65
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
66
        $stmt = $this->connection->prepare($sql);
67
        self::assertInstanceOf(Statement::class, $stmt);
68
69
        $stmt->bindValue(1, 1);
70
        $stmt->bindValue(2, 'foo');
71
        $stmt->execute();
72
73
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
74
        $row = array_change_key_case($row, CASE_LOWER);
75
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
76
    }
77
78
    public function testPrepareWithBindParam()
79
    {
80
        $paramInt = 1;
81
        $paramStr = 'foo';
82
83
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
84
        $stmt = $this->connection->prepare($sql);
85
        self::assertInstanceOf(Statement::class, $stmt);
86
87
        $stmt->bindParam(1, $paramInt);
88
        $stmt->bindParam(2, $paramStr);
89
        $stmt->execute();
90
91
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
92
        $row = array_change_key_case($row, CASE_LOWER);
93
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
94
    }
95
96
    public function testPrepareWithFetchAll()
97
    {
98
        $paramInt = 1;
99
        $paramStr = 'foo';
100
101
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
102
        $stmt = $this->connection->prepare($sql);
103
        self::assertInstanceOf(Statement::class, $stmt);
104
105
        $stmt->bindParam(1, $paramInt);
106
        $stmt->bindParam(2, $paramStr);
107
        $stmt->execute();
108
109
        $rows    = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
110
        $rows[0] = array_change_key_case($rows[0], CASE_LOWER);
111
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
112
    }
113
114
    /**
115
     * @group DBAL-228
116
     */
117
    public function testPrepareWithFetchAllBoth()
118
    {
119
        $paramInt = 1;
120
        $paramStr = 'foo';
121
122
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
123
        $stmt = $this->connection->prepare($sql);
124
        self::assertInstanceOf(Statement::class, $stmt);
125
126
        $stmt->bindParam(1, $paramInt);
127
        $stmt->bindParam(2, $paramStr);
128
        $stmt->execute();
129
130
        $rows    = $stmt->fetchAll(FetchMode::MIXED);
131
        $rows[0] = array_change_key_case($rows[0], CASE_LOWER);
132
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo', 0 => 1, 1 => 'foo'], $rows[0]);
133
    }
134
135
    public function testPrepareWithFetchColumn()
136
    {
137
        $paramInt = 1;
138
        $paramStr = 'foo';
139
140
        $sql  = 'SELECT test_int FROM fetch_table WHERE test_int = ? AND test_string = ?';
141
        $stmt = $this->connection->prepare($sql);
142
        self::assertInstanceOf(Statement::class, $stmt);
143
144
        $stmt->bindParam(1, $paramInt);
145
        $stmt->bindParam(2, $paramStr);
146
        $stmt->execute();
147
148
        $column = $stmt->fetchColumn();
149
        self::assertEquals(1, $column);
150
    }
151
152
    public function testPrepareWithIterator()
153
    {
154
        $paramInt = 1;
155
        $paramStr = 'foo';
156
157
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
158
        $stmt = $this->connection->prepare($sql);
159
        self::assertInstanceOf(Statement::class, $stmt);
160
161
        $stmt->bindParam(1, $paramInt);
162
        $stmt->bindParam(2, $paramStr);
163
        $stmt->execute();
164
165
        $rows = [];
166
        $stmt->setFetchMode(FetchMode::ASSOCIATIVE);
167
        foreach ($stmt as $row) {
168
            $rows[] = array_change_key_case($row, CASE_LOWER);
169
        }
170
171
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
172
    }
173
174
    public function testPrepareWithQuoted()
175
    {
176
        $table    = 'fetch_table';
177
        $paramInt = 1;
178
        $paramStr = 'foo';
179
180
        $stmt = $this->connection->prepare(sprintf(
181
            'SELECT test_int, test_string FROM %s WHERE test_int = %d AND test_string = %s',
182
            $this->connection->quoteIdentifier($table),
183
            $paramInt,
184
            $this->connection->quote($paramStr)
185
        ));
186
        self::assertInstanceOf(Statement::class, $stmt);
187
    }
188
189
    public function testPrepareWithExecuteParams()
190
    {
191
        $paramInt = 1;
192
        $paramStr = 'foo';
193
194
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
195
        $stmt = $this->connection->prepare($sql);
196
        self::assertInstanceOf(Statement::class, $stmt);
197
        $stmt->execute([$paramInt, $paramStr]);
198
199
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
200
        self::assertNotFalse($row);
201
        $row = array_change_key_case($row, CASE_LOWER);
202
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
203
    }
204
205
    public function testFetchAll()
206
    {
207
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
208
        $data = $this->connection->fetchAll($sql, [1, 'foo']);
209
210
        self::assertCount(1, $data);
211
212
        $row = $data[0];
213
        self::assertCount(2, $row);
214
215
        $row = array_change_key_case($row, CASE_LOWER);
216
        self::assertEquals(1, $row['test_int']);
217
        self::assertEquals('foo', $row['test_string']);
218
    }
219
220
    /**
221
     * @group DBAL-209
222
     */
223
    public function testFetchAllWithTypes()
224
    {
225
        $datetimeString = '2010-01-01 10:10:10';
226
        $datetime       = new DateTime($datetimeString);
227
228
        $sql  = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
229
        $data = $this->connection->fetchAll($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
230
231
        self::assertCount(1, $data);
232
233
        $row = $data[0];
234
        self::assertCount(2, $row);
235
236
        $row = array_change_key_case($row, CASE_LOWER);
237
        self::assertEquals(1, $row['test_int']);
238
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
239
    }
240
241
    /**
242
     * @group DBAL-209
243
     */
244
    public function testFetchAllWithMissingTypes()
245
    {
246
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
247
            $this->connection->getDriver() instanceof SQLSrvDriver) {
248
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
249
        }
250
251
        $datetimeString = '2010-01-01 10:10:10';
252
        $datetime       = new DateTime($datetimeString);
253
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
254
255
        $this->expectException(DBALException::class);
256
257
        $this->connection->fetchAll($sql, [1, $datetime]);
258
    }
259
260
    public function testFetchBoth()
261
    {
262
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
263
        $row = $this->connection->executeQuery($sql, [1, 'foo'])->fetch(FetchMode::MIXED);
264
265
        self::assertNotFalse($row);
266
267
        $row = array_change_key_case($row, CASE_LOWER);
268
269
        self::assertEquals(1, $row['test_int']);
270
        self::assertEquals('foo', $row['test_string']);
271
        self::assertEquals(1, $row[0]);
272
        self::assertEquals('foo', $row[1]);
273
    }
274
275
    public function testFetchNoResult()
276
    {
277
        self::assertFalse(
278
            $this->connection->executeQuery('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])->fetch()
279
        );
280
    }
281
282
    public function testFetchAssoc()
283
    {
284
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
285
        $row = $this->connection->fetchAssoc($sql, [1, 'foo']);
286
287
        self::assertNotFalse($row);
288
289
        $row = array_change_key_case($row, CASE_LOWER);
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type false; however, parameter $input of array_change_key_case() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

289
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
290
291
        self::assertEquals(1, $row['test_int']);
292
        self::assertEquals('foo', $row['test_string']);
293
    }
294
295
    public function testFetchAssocWithTypes()
296
    {
297
        $datetimeString = '2010-01-01 10:10:10';
298
        $datetime       = new DateTime($datetimeString);
299
300
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
301
        $row = $this->connection->fetchAssoc($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
302
303
        self::assertNotFalse($row);
304
305
        $row = array_change_key_case($row, CASE_LOWER);
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type false; however, parameter $input of array_change_key_case() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

305
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
306
307
        self::assertEquals(1, $row['test_int']);
308
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
309
    }
310
311
    public function testFetchAssocWithMissingTypes()
312
    {
313
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
314
            $this->connection->getDriver() instanceof SQLSrvDriver) {
315
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
316
        }
317
318
        $datetimeString = '2010-01-01 10:10:10';
319
        $datetime       = new DateTime($datetimeString);
320
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
321
322
        $this->expectException(DBALException::class);
323
324
        $this->connection->fetchAssoc($sql, [1, $datetime]);
325
    }
326
327
    public function testFetchArray()
328
    {
329
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
330
        $row = $this->connection->fetchArray($sql, [1, 'foo']);
331
332
        self::assertEquals(1, $row[0]);
333
        self::assertEquals('foo', $row[1]);
334
    }
335
336
    public function testFetchArrayWithTypes()
337
    {
338
        $datetimeString = '2010-01-01 10:10:10';
339
        $datetime       = new DateTime($datetimeString);
340
341
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
342
        $row = $this->connection->fetchArray($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
343
344
        self::assertNotFalse($row);
345
346
        $row = array_change_key_case($row, CASE_LOWER);
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type false; however, parameter $input of array_change_key_case() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

346
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
347
348
        self::assertEquals(1, $row[0]);
349
        self::assertStringStartsWith($datetimeString, $row[1]);
350
    }
351
352
    public function testFetchArrayWithMissingTypes()
353
    {
354
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
355
            $this->connection->getDriver() instanceof SQLSrvDriver) {
356
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
357
        }
358
359
        $datetimeString = '2010-01-01 10:10:10';
360
        $datetime       = new DateTime($datetimeString);
361
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
362
363
        $this->expectException(DBALException::class);
364
365
        $this->connection->fetchArray($sql, [1, $datetime]);
366
    }
367
368
    public function testFetchColumn()
369
    {
370
        $sql     = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
371
        $testInt = $this->connection->fetchColumn($sql, [1, 'foo'], 0);
372
373
        self::assertEquals(1, $testInt);
374
375
        $sql        = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
376
        $testString = $this->connection->fetchColumn($sql, [1, 'foo'], 1);
377
378
        self::assertEquals('foo', $testString);
379
    }
380
381
    public function testFetchColumnWithTypes()
382
    {
383
        $datetimeString = '2010-01-01 10:10:10';
384
        $datetime       = new DateTime($datetimeString);
385
386
        $sql    = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
387
        $column = $this->connection->fetchColumn($sql, [1, $datetime], 1, [ParameterType::STRING, Type::DATETIME]);
388
389
        self::assertNotFalse($column);
390
391
        self::assertStringStartsWith($datetimeString, $column);
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type false; however, parameter $string of PHPUnit\Framework\Assert::assertStringStartsWith() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

391
        self::assertStringStartsWith($datetimeString, /** @scrutinizer ignore-type */ $column);
Loading history...
392
    }
393
394
    public function testFetchColumnWithMissingTypes()
395
    {
396
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
397
            $this->connection->getDriver() instanceof SQLSrvDriver) {
398
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
399
        }
400
401
        $datetimeString = '2010-01-01 10:10:10';
402
        $datetime       = new DateTime($datetimeString);
403
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
404
405
        $this->expectException(DBALException::class);
406
407
        $this->connection->fetchColumn($sql, [1, $datetime], 1);
408
    }
409
410
    /**
411
     * @group DDC-697
412
     */
413
    public function testExecuteQueryBindDateTimeType()
414
    {
415
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
416
        $stmt = $this->connection->executeQuery(
417
            $sql,
418
            [1 => new DateTime('2010-01-01 10:10:10')],
419
            [1 => Type::DATETIME]
420
        );
421
422
        self::assertEquals(1, $stmt->fetchColumn());
423
    }
424
425
    /**
426
     * @group DDC-697
427
     */
428
    public function testExecuteUpdateBindDateTimeType()
429
    {
430
        $datetime = new DateTime('2010-02-02 20:20:20');
431
432
        $sql          = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)';
433
        $affectedRows = $this->connection->executeUpdate($sql, [
434
            1 => 50,
435
            2 => 'foo',
436
            3 => $datetime,
437
        ], [
438
            1 => ParameterType::INTEGER,
439
            2 => ParameterType::STRING,
440
            3 => Type::DATETIME,
441
        ]);
442
443
        self::assertEquals(1, $affectedRows);
444
        self::assertEquals(1, $this->connection->executeQuery(
445
            'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
446
            [1 => $datetime],
447
            [1 => Type::DATETIME]
448
        )->fetchColumn());
449
    }
450
451
    /**
452
     * @group DDC-697
453
     */
454
    public function testPrepareQueryBindValueDateTimeType()
455
    {
456
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
457
        $stmt = $this->connection->prepare($sql);
458
        $stmt->bindValue(1, new DateTime('2010-01-01 10:10:10'), Type::DATETIME);
459
        $stmt->execute();
460
461
        self::assertEquals(1, $stmt->fetchColumn());
462
    }
463
464
    /**
465
     * @group DBAL-78
466
     */
467
    public function testNativeArrayListSupport()
468
    {
469
        for ($i = 100; $i < 110; $i++) {
470
            $this->connection->insert('fetch_table', ['test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10']);
471
        }
472
473
        $stmt = $this->connection->executeQuery(
474
            'SELECT test_int FROM fetch_table WHERE test_int IN (?)',
475
            [[100, 101, 102, 103, 104]],
476
            [Connection::PARAM_INT_ARRAY]
477
        );
478
479
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
480
        self::assertCount(5, $data);
481
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
482
483
        $stmt = $this->connection->executeQuery(
484
            'SELECT test_int FROM fetch_table WHERE test_string IN (?)',
485
            [['foo100', 'foo101', 'foo102', 'foo103', 'foo104']],
486
            [Connection::PARAM_STR_ARRAY]
487
        );
488
489
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
490
        self::assertCount(5, $data);
491
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
492
    }
493
494
    /**
495
     * @dataProvider getTrimExpressionData
496
     */
497
    public function testTrimExpression($value, $position, $char, $expectedResult)
498
    {
499
        $sql = 'SELECT ' .
500
            $this->connection->getDatabasePlatform()->getTrimExpression($value, $position, $char) . ' AS trimmed ' .
501
            'FROM fetch_table';
502
503
        $row = $this->connection->fetchAssoc($sql);
504
        $row = array_change_key_case($row, CASE_LOWER);
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type false; however, parameter $input of array_change_key_case() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

504
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
505
506
        self::assertEquals($expectedResult, $row['trimmed']);
507
    }
508
509
    public function getTrimExpressionData()
510
    {
511
        return [
512
            ['test_string', TrimMode::UNSPECIFIED, null, 'foo'],
513
            ['test_string', TrimMode::LEADING, null, 'foo'],
514
            ['test_string', TrimMode::TRAILING, null, 'foo'],
515
            ['test_string', TrimMode::BOTH, null, 'foo'],
516
            ['test_string', TrimMode::UNSPECIFIED, "'f'", 'oo'],
517
            ['test_string', TrimMode::UNSPECIFIED, "'o'", 'f'],
518
            ['test_string', TrimMode::UNSPECIFIED, "'.'", 'foo'],
519
            ['test_string', TrimMode::LEADING, "'f'", 'oo'],
520
            ['test_string', TrimMode::LEADING, "'o'", 'foo'],
521
            ['test_string', TrimMode::LEADING, "'.'", 'foo'],
522
            ['test_string', TrimMode::TRAILING, "'f'", 'foo'],
523
            ['test_string', TrimMode::TRAILING, "'o'", 'f'],
524
            ['test_string', TrimMode::TRAILING, "'.'", 'foo'],
525
            ['test_string', TrimMode::BOTH, "'f'", 'oo'],
526
            ['test_string', TrimMode::BOTH, "'o'", 'f'],
527
            ['test_string', TrimMode::BOTH, "'.'", 'foo'],
528
            ["' foo '", TrimMode::UNSPECIFIED, null, 'foo'],
529
            ["' foo '", TrimMode::LEADING, null, 'foo '],
530
            ["' foo '", TrimMode::TRAILING, null, ' foo'],
531
            ["' foo '", TrimMode::BOTH, null, 'foo'],
532
            ["' foo '", TrimMode::UNSPECIFIED, "'f'", ' foo '],
533
            ["' foo '", TrimMode::UNSPECIFIED, "'o'", ' foo '],
534
            ["' foo '", TrimMode::UNSPECIFIED, "'.'", ' foo '],
535
            ["' foo '", TrimMode::UNSPECIFIED, "' '", 'foo'],
536
            ["' foo '", TrimMode::LEADING, "'f'", ' foo '],
537
            ["' foo '", TrimMode::LEADING, "'o'", ' foo '],
538
            ["' foo '", TrimMode::LEADING, "'.'", ' foo '],
539
            ["' foo '", TrimMode::LEADING, "' '", 'foo '],
540
            ["' foo '", TrimMode::TRAILING, "'f'", ' foo '],
541
            ["' foo '", TrimMode::TRAILING, "'o'", ' foo '],
542
            ["' foo '", TrimMode::TRAILING, "'.'", ' foo '],
543
            ["' foo '", TrimMode::TRAILING, "' '", ' foo'],
544
            ["' foo '", TrimMode::BOTH, "'f'", ' foo '],
545
            ["' foo '", TrimMode::BOTH, "'o'", ' foo '],
546
            ["' foo '", TrimMode::BOTH, "'.'", ' foo '],
547
            ["' foo '", TrimMode::BOTH, "' '", 'foo'],
548
        ];
549
    }
550
551
    public function testTrimExpressionInvalidMode() : void
552
    {
553
        $this->expectException(InvalidArgumentException::class);
554
        $this->connection->getDatabasePlatform()->getTrimExpression('Trim me!', 0xBEEF);
555
    }
556
557
    /**
558
     * @group DDC-1014
559
     */
560
    public function testDateArithmetics()
561
    {
562
        $p    = $this->connection->getDatabasePlatform();
563
        $sql  = 'SELECT ';
564
        $sql .= $p->getDateAddSecondsExpression('test_datetime', 1) . ' AS add_seconds, ';
565
        $sql .= $p->getDateSubSecondsExpression('test_datetime', 1) . ' AS sub_seconds, ';
566
        $sql .= $p->getDateAddMinutesExpression('test_datetime', 5) . ' AS add_minutes, ';
567
        $sql .= $p->getDateSubMinutesExpression('test_datetime', 5) . ' AS sub_minutes, ';
568
        $sql .= $p->getDateAddHourExpression('test_datetime', 3) . ' AS add_hour, ';
569
        $sql .= $p->getDateSubHourExpression('test_datetime', 3) . ' AS sub_hour, ';
570
        $sql .= $p->getDateAddDaysExpression('test_datetime', 10) . ' AS add_days, ';
571
        $sql .= $p->getDateSubDaysExpression('test_datetime', 10) . ' AS sub_days, ';
572
        $sql .= $p->getDateAddWeeksExpression('test_datetime', 1) . ' AS add_weeks, ';
573
        $sql .= $p->getDateSubWeeksExpression('test_datetime', 1) . ' AS sub_weeks, ';
574
        $sql .= $p->getDateAddMonthExpression('test_datetime', 2) . ' AS add_month, ';
575
        $sql .= $p->getDateSubMonthExpression('test_datetime', 2) . ' AS sub_month, ';
576
        $sql .= $p->getDateAddQuartersExpression('test_datetime', 3) . ' AS add_quarters, ';
577
        $sql .= $p->getDateSubQuartersExpression('test_datetime', 3) . ' AS sub_quarters, ';
578
        $sql .= $p->getDateAddYearsExpression('test_datetime', 6) . ' AS add_years, ';
579
        $sql .= $p->getDateSubYearsExpression('test_datetime', 6) . ' AS sub_years ';
580
        $sql .= 'FROM fetch_table';
581
582
        $row = $this->connection->fetchAssoc($sql);
583
        $row = array_change_key_case($row, CASE_LOWER);
584
585
        self::assertEquals('2010-01-01 10:10:11', date('Y-m-d H:i:s', strtotime($row['add_seconds'])), 'Adding second should end up on 2010-01-01 10:10:11');
586
        self::assertEquals('2010-01-01 10:10:09', date('Y-m-d H:i:s', strtotime($row['sub_seconds'])), 'Subtracting second should end up on 2010-01-01 10:10:09');
587
        self::assertEquals('2010-01-01 10:15:10', date('Y-m-d H:i:s', strtotime($row['add_minutes'])), 'Adding minutes should end up on 2010-01-01 10:15:10');
588
        self::assertEquals('2010-01-01 10:05:10', date('Y-m-d H:i:s', strtotime($row['sub_minutes'])), 'Subtracting minutes should end up on 2010-01-01 10:05:10');
589
        self::assertEquals('2010-01-01 13:10', date('Y-m-d H:i', strtotime($row['add_hour'])), 'Adding date should end up on 2010-01-01 13:10');
590
        self::assertEquals('2010-01-01 07:10', date('Y-m-d H:i', strtotime($row['sub_hour'])), 'Subtracting date should end up on 2010-01-01 07:10');
591
        self::assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), 'Adding date should end up on 2010-01-11');
592
        self::assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), 'Subtracting date should end up on 2009-12-22');
593
        self::assertEquals('2010-01-08', date('Y-m-d', strtotime($row['add_weeks'])), 'Adding week should end up on 2010-01-08');
594
        self::assertEquals('2009-12-25', date('Y-m-d', strtotime($row['sub_weeks'])), 'Subtracting week should end up on 2009-12-25');
595
        self::assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), 'Adding month should end up on 2010-03-01');
596
        self::assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), 'Subtracting month should end up on 2009-11-01');
597
        self::assertEquals('2010-10-01', date('Y-m-d', strtotime($row['add_quarters'])), 'Adding quarters should end up on 2010-04-01');
598
        self::assertEquals('2009-04-01', date('Y-m-d', strtotime($row['sub_quarters'])), 'Subtracting quarters should end up on 2009-10-01');
599
        self::assertEquals('2016-01-01', date('Y-m-d', strtotime($row['add_years'])), 'Adding years should end up on 2016-01-01');
600
        self::assertEquals('2004-01-01', date('Y-m-d', strtotime($row['sub_years'])), 'Subtracting years should end up on 2004-01-01');
601
    }
602
603
    public function testSqliteDateArithmeticWithDynamicInterval()
604
    {
605
        $platform = $this->connection->getDatabasePlatform();
606
607
        if (! $platform instanceof SqlitePlatform) {
608
            $this->markTestSkipped('test is for sqlite only');
609
        }
610
611
        $table = new Table('fetch_table_date_math');
612
        $table->addColumn('test_date', 'date');
613
        $table->addColumn('test_days', 'integer');
614
        $table->setPrimaryKey(['test_date']);
615
616
        $sm = $this->connection->getSchemaManager();
617
        $sm->createTable($table);
618
619
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-01-01', 'test_days' => 10]);
620
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-06-01', 'test_days' => 20]);
621
622
        $sql  = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
623
        $sql .= $platform->getDateSubDaysExpression('test_date', 'test_days') . " < '2010-05-12'";
0 ignored issues
show
Bug introduced by
'test_days' of type string is incompatible with the type integer expected by parameter $days of Doctrine\DBAL\Platforms\...DateSubDaysExpression(). ( Ignorable by Annotation )

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

623
        $sql .= $platform->getDateSubDaysExpression('test_date', /** @scrutinizer ignore-type */ 'test_days') . " < '2010-05-12'";
Loading history...
624
625
        $rowCount = $this->connection->fetchColumn($sql, [], 0);
626
627
        $this->assertEquals(1, $rowCount);
628
    }
629
630
    public function testLocateExpression()
631
    {
632
        $platform = $this->connection->getDatabasePlatform();
633
634
        $sql  = 'SELECT ';
635
        $sql .= $platform->getLocateExpression('test_string', "'oo'") . ' AS locate1, ';
636
        $sql .= $platform->getLocateExpression('test_string', "'foo'") . ' AS locate2, ';
637
        $sql .= $platform->getLocateExpression('test_string', "'bar'") . ' AS locate3, ';
638
        $sql .= $platform->getLocateExpression('test_string', 'test_string') . ' AS locate4, ';
639
        $sql .= $platform->getLocateExpression("'foo'", 'test_string') . ' AS locate5, ';
640
        $sql .= $platform->getLocateExpression("'barfoobaz'", 'test_string') . ' AS locate6, ';
641
        $sql .= $platform->getLocateExpression("'bar'", 'test_string') . ' AS locate7, ';
642
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 2) . ' AS locate8, ';
643
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 3) . ' AS locate9 ';
644
        $sql .= 'FROM fetch_table';
645
646
        $row = $this->connection->fetchAssoc($sql);
647
        $row = array_change_key_case($row, CASE_LOWER);
648
649
        self::assertEquals(2, $row['locate1']);
650
        self::assertEquals(1, $row['locate2']);
651
        self::assertEquals(0, $row['locate3']);
652
        self::assertEquals(1, $row['locate4']);
653
        self::assertEquals(1, $row['locate5']);
654
        self::assertEquals(4, $row['locate6']);
655
        self::assertEquals(0, $row['locate7']);
656
        self::assertEquals(2, $row['locate8']);
657
        self::assertEquals(0, $row['locate9']);
658
    }
659
660
    /**
661
     * @dataProvider substringExpressionProvider
662
     */
663
    public function testSubstringExpression(string $string, string $start, ?string $length, string $expected) : void
664
    {
665
        $platform = $this->connection->getDatabasePlatform();
666
667
        $query = $platform->getDummySelectSQL(
668
            $platform->getSubstringExpression($string, $start, $length)
669
        );
670
671
        $this->assertEquals($expected, $this->connection->fetchColumn($query));
672
    }
673
674
    /**
675
     * @return mixed[][]
676
     */
677
    public static function substringExpressionProvider() : iterable
678
    {
679
        return [
680
            'start-no-length' => [
681
                "'abcdef'",
682
                '3',
683
                null,
684
                'cdef',
685
            ],
686
            'start-with-length' => [
687
                "'abcdef'",
688
                '2',
689
                '4',
690
                'bcde',
691
            ],
692
            'expressions' => [
693
                "'abcdef'",
694
                '1 + 1',
695
                '1 + 1',
696
                'bc',
697
            ],
698
        ];
699
    }
700
701
    public function testQuoteSQLInjection()
702
    {
703
        $sql  = 'SELECT * FROM fetch_table WHERE test_string = ' . $this->connection->quote("bar' OR '1'='1");
704
        $rows = $this->connection->fetchAll($sql);
705
706
        self::assertCount(0, $rows, 'no result should be returned, otherwise SQL injection is possible');
707
    }
708
709
    /**
710
     * @group DDC-1213
711
     */
712
    public function testBitComparisonExpressionSupport()
713
    {
714
        $this->connection->exec('DELETE FROM fetch_table');
715
        $platform = $this->connection->getDatabasePlatform();
716
        $bitmap   = [];
717
718
        for ($i = 2; $i < 9; $i += 2) {
719
            $bitmap[$i] = [
720
                'bit_or'    => ($i | 2),
721
                'bit_and'   => ($i & 2),
722
            ];
723
            $this->connection->insert('fetch_table', [
724
                'test_int'      => $i,
725
                'test_string'   => json_encode($bitmap[$i]),
726
                'test_datetime' => '2010-01-01 10:10:10',
727
            ]);
728
        }
729
730
        $sql[] = 'SELECT ';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.
Loading history...
731
        $sql[] = 'test_int, ';
732
        $sql[] = 'test_string, ';
733
        $sql[] = $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or, ';
734
        $sql[] = $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and ';
735
        $sql[] = 'FROM fetch_table';
736
737
        $stmt = $this->connection->executeQuery(implode(PHP_EOL, $sql));
738
        $data = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
739
740
        self::assertCount(4, $data);
741
        self::assertEquals(count($bitmap), count($data));
742
        foreach ($data as $row) {
743
            $row = array_change_key_case($row, CASE_LOWER);
744
745
            self::assertArrayHasKey('test_int', $row);
746
747
            $id = $row['test_int'];
748
749
            self::assertArrayHasKey($id, $bitmap);
750
            self::assertArrayHasKey($id, $bitmap);
751
752
            self::assertArrayHasKey('bit_or', $row);
753
            self::assertArrayHasKey('bit_and', $row);
754
755
            self::assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
756
            self::assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
757
        }
758
    }
759
760
    public function testSetDefaultFetchMode()
761
    {
762
        $stmt = $this->connection->query('SELECT * FROM fetch_table');
763
        $stmt->setFetchMode(FetchMode::NUMERIC);
764
765
        $row = array_keys($stmt->fetch());
766
        self::assertCount(0, array_filter($row, static function ($v) {
767
            return ! is_numeric($v);
768
        }), 'should be no non-numerical elements in the result.');
769
    }
770
771
    /**
772
     * @group DBAL-1091
773
     */
774
    public function testFetchAllStyleObject()
775
    {
776
        $this->setupFixture();
777
778
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
779
        $stmt = $this->connection->prepare($sql);
780
781
        $stmt->execute();
782
783
        $results = $stmt->fetchAll(FetchMode::STANDARD_OBJECT);
784
785
        self::assertCount(1, $results);
786
        self::assertInstanceOf('stdClass', $results[0]);
787
788
        self::assertEquals(
789
            1,
790
            property_exists($results[0], 'test_int') ? $results[0]->test_int : $results[0]->TEST_INT
791
        );
792
        self::assertEquals(
793
            'foo',
794
            property_exists($results[0], 'test_string') ? $results[0]->test_string : $results[0]->TEST_STRING
795
        );
796
        self::assertStringStartsWith(
797
            '2010-01-01 10:10:10',
798
            property_exists($results[0], 'test_datetime') ? $results[0]->test_datetime : $results[0]->TEST_DATETIME
799
        );
800
    }
801
802
    /**
803
     * @group DBAL-196
804
     */
805
    public function testFetchAllSupportFetchClass()
806
    {
807
        $this->beforeFetchClassTest();
808
        $this->setupFixture();
809
810
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
811
        $stmt = $this->connection->prepare($sql);
812
        $stmt->execute();
813
814
        $results = $stmt->fetchAll(
815
            FetchMode::CUSTOM_OBJECT,
816
            MyFetchClass::class
817
        );
818
819
        self::assertCount(1, $results);
820
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
821
822
        self::assertEquals(1, $results[0]->test_int);
823
        self::assertEquals('foo', $results[0]->test_string);
824
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
825
    }
826
827
    /**
828
     * @group DBAL-241
829
     */
830
    public function testFetchAllStyleColumn()
831
    {
832
        $sql = 'DELETE FROM fetch_table';
833
        $this->connection->executeUpdate($sql);
834
835
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo']);
836
        $this->connection->insert('fetch_table', ['test_int' => 10, 'test_string' => 'foo']);
837
838
        $sql  = 'SELECT test_int FROM fetch_table';
839
        $rows = $this->connection->query($sql)->fetchAll(FetchMode::COLUMN);
840
841
        self::assertEquals([1, 10], $rows);
842
    }
843
844
    /**
845
     * @group DBAL-214
846
     */
847
    public function testSetFetchModeClassFetchAll()
848
    {
849
        $this->beforeFetchClassTest();
850
        $this->setupFixture();
851
852
        $sql  = 'SELECT * FROM fetch_table';
853
        $stmt = $this->connection->query($sql);
854
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
855
856
        $results = $stmt->fetchAll();
857
858
        self::assertCount(1, $results);
859
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
860
861
        self::assertEquals(1, $results[0]->test_int);
862
        self::assertEquals('foo', $results[0]->test_string);
863
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
864
    }
865
866
    /**
867
     * @group DBAL-214
868
     */
869
    public function testSetFetchModeClassFetch()
870
    {
871
        $this->beforeFetchClassTest();
872
        $this->setupFixture();
873
874
        $sql  = 'SELECT * FROM fetch_table';
875
        $stmt = $this->connection->query($sql);
876
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
877
878
        $results = [];
879
        while ($row = $stmt->fetch()) {
880
            $results[] = $row;
881
        }
882
883
        self::assertCount(1, $results);
884
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
885
886
        self::assertEquals(1, $results[0]->test_int);
887
        self::assertEquals('foo', $results[0]->test_string);
888
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
889
    }
890
891
    /**
892
     * @group DBAL-257
893
     */
894
    public function testEmptyFetchColumnReturnsFalse()
895
    {
896
        $this->connection->beginTransaction();
897
        $this->connection->exec('DELETE FROM fetch_table');
898
        self::assertFalse($this->connection->fetchColumn('SELECT test_int FROM fetch_table'));
899
        self::assertFalse($this->connection->query('SELECT test_int FROM fetch_table')->fetchColumn());
900
        $this->connection->rollBack();
901
    }
902
903
    /**
904
     * @group DBAL-339
905
     */
906
    public function testSetFetchModeOnDbalStatement()
907
    {
908
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
909
        $stmt = $this->connection->executeQuery($sql, [1, 'foo']);
910
        $stmt->setFetchMode(FetchMode::NUMERIC);
911
912
        $row = $stmt->fetch();
913
914
        self::assertArrayHasKey(0, $row);
915
        self::assertArrayHasKey(1, $row);
916
        self::assertFalse($stmt->fetch());
917
    }
918
919
    /**
920
     * @group DBAL-435
921
     */
922
    public function testEmptyParameters()
923
    {
924
        $sql  = 'SELECT * FROM fetch_table WHERE test_int IN (?)';
925
        $stmt = $this->connection->executeQuery($sql, [[]], [Connection::PARAM_INT_ARRAY]);
926
        $rows = $stmt->fetchAll();
927
928
        self::assertEquals([], $rows);
929
    }
930
931
    /**
932
     * @group DBAL-1028
933
     */
934
    public function testFetchColumnNullValue()
935
    {
936
        $this->connection->executeUpdate(
937
            'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)',
938
            [2, 'foo']
939
        );
940
941
        self::assertNull(
942
            $this->connection->fetchColumn('SELECT test_datetime FROM fetch_table WHERE test_int = ?', [2])
943
        );
944
    }
945
946
    /**
947
     * @group DBAL-1028
948
     */
949
    public function testFetchColumnNoResult()
950
    {
951
        self::assertFalse(
952
            $this->connection->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])
953
        );
954
    }
955
956
    private function setupFixture()
957
    {
958
        $this->connection->exec('DELETE FROM fetch_table');
959
        $this->connection->insert('fetch_table', [
960
            'test_int'      => 1,
961
            'test_string'   => 'foo',
962
            'test_datetime' => '2010-01-01 10:10:10',
963
        ]);
964
    }
965
966
    private function beforeFetchClassTest()
967
    {
968
        $driver = $this->connection->getDriver();
969
970
        if ($driver instanceof Oci8Driver) {
971
            $this->markTestSkipped('Not supported by OCI8');
972
        }
973
974
        if ($driver instanceof MySQLiDriver) {
975
            $this->markTestSkipped('Mysqli driver dont support this feature.');
976
        }
977
978
        if (! $driver instanceof PDOOracleDriver) {
979
            return;
980
        }
981
982
        /** @var PDOConnection $connection */
983
        $connection = $this->connection
984
            ->getWrappedConnection();
985
        $connection->getWrappedConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
986
    }
987
}
988
989
class MyFetchClass
990
{
991
    /** @var int */
992
    public $test_int;
993
994
    /** @var string */
995
    public $test_string;
996
997
    /** @var string */
998
    public $test_datetime;
999
}
1000