Completed
Pull Request — 3.0.x (#3070)
by Sergei
63:18
created

DataAccessTest::testFetchAssocWithMissingTypes()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

285
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
286
287
        self::assertEquals(1, $row['test_int']);
288
        self::assertEquals('foo', $row['test_string']);
289
    }
290
291
    public function testFetchAssocWithTypes() : void
292
    {
293
        $datetimeString = '2010-01-01 10:10:10';
294
        $datetime       = new DateTime($datetimeString);
295
296
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
297
        $row = $this->connection->fetchAssoc(
298
            $sql,
299
            [1, $datetime],
300
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
301
        );
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() : void
312
    {
313
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
314
            $this->connection->getDriver() instanceof SQLSrvDriver) {
315
            self::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() : void
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() : void
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(
343
            $sql,
344
            [1, $datetime],
345
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
346
        );
347
348
        self::assertNotFalse($row);
349
350
        $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

350
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
351
352
        self::assertEquals(1, $row[0]);
353
        self::assertStringStartsWith($datetimeString, $row[1]);
354
    }
355
356
    public function testFetchArrayWithMissingTypes() : void
357
    {
358
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
359
            $this->connection->getDriver() instanceof SQLSrvDriver) {
360
            self::markTestSkipped('mysqli and sqlsrv actually supports this');
361
        }
362
363
        $datetimeString = '2010-01-01 10:10:10';
364
        $datetime       = new DateTime($datetimeString);
365
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
366
367
        $this->expectException(DBALException::class);
368
369
        $this->connection->fetchArray($sql, [1, $datetime]);
370
    }
371
372
    public function testFetchColumn() : void
373
    {
374
        $sql     = 'SELECT test_int FROM fetch_table WHERE test_int = ? AND test_string = ?';
375
        $testInt = $this->connection->fetchColumn($sql, [1, 'foo']);
376
377
        self::assertEquals(1, $testInt);
378
379
        $sql        = 'SELECT test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
380
        $testString = $this->connection->fetchColumn($sql, [1, 'foo']);
381
382
        self::assertEquals('foo', $testString);
383
    }
384
385
    public function testFetchColumnWithTypes() : void
386
    {
387
        $datetimeString = '2010-01-01 10:10:10';
388
        $datetime       = new DateTime($datetimeString);
389
390
        $sql    = 'SELECT test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
391
        $column = $this->connection->fetchColumn(
392
            $sql,
393
            [1, $datetime],
394
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
395
        );
396
397
        self::assertIsString($column);
398
399
        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

399
        self::assertStringStartsWith($datetimeString, /** @scrutinizer ignore-type */ $column);
Loading history...
400
    }
401
402
    public function testFetchColumnWithMissingTypes() : void
403
    {
404
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
405
            $this->connection->getDriver() instanceof SQLSrvDriver) {
406
            self::markTestSkipped('mysqli and sqlsrv actually supports this');
407
        }
408
409
        $datetimeString = '2010-01-01 10:10:10';
410
        $datetime       = new DateTime($datetimeString);
411
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
412
413
        $this->expectException(DBALException::class);
414
415
        $this->connection->fetchColumn($sql, [1, $datetime]);
416
    }
417
418
    /**
419
     * @group DDC-697
420
     */
421
    public function testExecuteQueryBindDateTimeType() : void
422
    {
423
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
424
        $stmt = $this->connection->executeQuery(
425
            $sql,
426
            [1 => new DateTime('2010-01-01 10:10:10')],
427
            [1 => Types::DATETIME_MUTABLE]
428
        );
429
430
        self::assertEquals(1, $stmt->fetchColumn());
431
    }
432
433
    /**
434
     * @group DDC-697
435
     */
436
    public function testExecuteUpdateBindDateTimeType() : void
437
    {
438
        $datetime = new DateTime('2010-02-02 20:20:20');
439
440
        $sql          = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)';
441
        $affectedRows = $this->connection->executeUpdate($sql, [
442
            1 => 50,
443
            2 => 'foo',
444
            3 => $datetime,
445
        ], [
446
            1 => ParameterType::INTEGER,
447
            2 => ParameterType::STRING,
448
            3 => Types::DATETIME_MUTABLE,
449
        ]);
450
451
        self::assertEquals(1, $affectedRows);
452
        self::assertEquals(1, $this->connection->executeQuery(
453
            'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
454
            [1 => $datetime],
455
            [1 => Types::DATETIME_MUTABLE]
456
        )->fetchColumn());
457
    }
458
459
    /**
460
     * @group DDC-697
461
     */
462
    public function testPrepareQueryBindValueDateTimeType() : void
463
    {
464
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
465
        $stmt = $this->connection->prepare($sql);
466
        $stmt->bindValue(1, new DateTime('2010-01-01 10:10:10'), Types::DATETIME_MUTABLE);
467
        $stmt->execute();
468
469
        self::assertEquals(1, $stmt->fetchColumn());
470
    }
471
472
    /**
473
     * @group DBAL-78
474
     */
475
    public function testNativeArrayListSupport() : void
476
    {
477
        for ($i = 100; $i < 110; $i++) {
478
            $this->connection->insert('fetch_table', ['test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10']);
479
        }
480
481
        $stmt = $this->connection->executeQuery(
482
            'SELECT test_int FROM fetch_table WHERE test_int IN (?)',
483
            [[100, 101, 102, 103, 104]],
484
            [Connection::PARAM_INT_ARRAY]
485
        );
486
487
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
488
        self::assertCount(5, $data);
489
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
490
491
        $stmt = $this->connection->executeQuery(
492
            'SELECT test_int FROM fetch_table WHERE test_string IN (?)',
493
            [['foo100', 'foo101', 'foo102', 'foo103', 'foo104']],
494
            [Connection::PARAM_STR_ARRAY]
495
        );
496
497
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
498
        self::assertCount(5, $data);
499
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
500
    }
501
502
    /**
503
     * @param string|false $char
504
     *
505
     * @dataProvider getTrimExpressionData
506
     */
507
    public function testTrimExpression(string $value, int $position, $char, string $expectedResult) : void
508
    {
509
        $sql = 'SELECT ' .
510
            $this->connection->getDatabasePlatform()->getTrimExpression($value, $position, $char) . ' AS trimmed ' .
511
            'FROM fetch_table';
512
513
        $row = $this->connection->fetchAssoc($sql);
514
        $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

514
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
515
516
        self::assertEquals($expectedResult, $row['trimmed']);
517
    }
518
519
    /**
520
     * @return array<int, array<int, mixed>>
521
     */
522
    public static function getTrimExpressionData() : iterable
523
    {
524
        return [
525
            ['test_string', TrimMode::UNSPECIFIED, false, 'foo'],
526
            ['test_string', TrimMode::LEADING, false, 'foo'],
527
            ['test_string', TrimMode::TRAILING, false, 'foo'],
528
            ['test_string', TrimMode::BOTH, false, 'foo'],
529
            ['test_string', TrimMode::UNSPECIFIED, "'f'", 'oo'],
530
            ['test_string', TrimMode::UNSPECIFIED, "'o'", 'f'],
531
            ['test_string', TrimMode::UNSPECIFIED, "'.'", 'foo'],
532
            ['test_string', TrimMode::LEADING, "'f'", 'oo'],
533
            ['test_string', TrimMode::LEADING, "'o'", 'foo'],
534
            ['test_string', TrimMode::LEADING, "'.'", 'foo'],
535
            ['test_string', TrimMode::TRAILING, "'f'", 'foo'],
536
            ['test_string', TrimMode::TRAILING, "'o'", 'f'],
537
            ['test_string', TrimMode::TRAILING, "'.'", 'foo'],
538
            ['test_string', TrimMode::BOTH, "'f'", 'oo'],
539
            ['test_string', TrimMode::BOTH, "'o'", 'f'],
540
            ['test_string', TrimMode::BOTH, "'.'", 'foo'],
541
            ["' foo '", TrimMode::UNSPECIFIED, false, 'foo'],
542
            ["' foo '", TrimMode::LEADING, false, 'foo '],
543
            ["' foo '", TrimMode::TRAILING, false, ' foo'],
544
            ["' foo '", TrimMode::BOTH, false, 'foo'],
545
            ["' foo '", TrimMode::UNSPECIFIED, "'f'", ' foo '],
546
            ["' foo '", TrimMode::UNSPECIFIED, "'o'", ' foo '],
547
            ["' foo '", TrimMode::UNSPECIFIED, "'.'", ' foo '],
548
            ["' foo '", TrimMode::UNSPECIFIED, "' '", 'foo'],
549
            ["' foo '", TrimMode::LEADING, "'f'", ' foo '],
550
            ["' foo '", TrimMode::LEADING, "'o'", ' foo '],
551
            ["' foo '", TrimMode::LEADING, "'.'", ' foo '],
552
            ["' foo '", TrimMode::LEADING, "' '", 'foo '],
553
            ["' foo '", TrimMode::TRAILING, "'f'", ' foo '],
554
            ["' foo '", TrimMode::TRAILING, "'o'", ' foo '],
555
            ["' foo '", TrimMode::TRAILING, "'.'", ' foo '],
556
            ["' foo '", TrimMode::TRAILING, "' '", ' foo'],
557
            ["' foo '", TrimMode::BOTH, "'f'", ' foo '],
558
            ["' foo '", TrimMode::BOTH, "'o'", ' foo '],
559
            ["' foo '", TrimMode::BOTH, "'.'", ' foo '],
560
            ["' foo '", TrimMode::BOTH, "' '", 'foo'],
561
        ];
562
    }
563
564
    /**
565
     * @group DDC-1014
566
     */
567
    public function testDateArithmetics() : void
568
    {
569
        $p    = $this->connection->getDatabasePlatform();
570
        $sql  = 'SELECT ';
571
        $sql .= $p->getDateAddSecondsExpression('test_datetime', 1) . ' AS add_seconds, ';
572
        $sql .= $p->getDateSubSecondsExpression('test_datetime', 1) . ' AS sub_seconds, ';
573
        $sql .= $p->getDateAddMinutesExpression('test_datetime', 5) . ' AS add_minutes, ';
574
        $sql .= $p->getDateSubMinutesExpression('test_datetime', 5) . ' AS sub_minutes, ';
575
        $sql .= $p->getDateAddHourExpression('test_datetime', 3) . ' AS add_hour, ';
576
        $sql .= $p->getDateSubHourExpression('test_datetime', 3) . ' AS sub_hour, ';
577
        $sql .= $p->getDateAddDaysExpression('test_datetime', 10) . ' AS add_days, ';
578
        $sql .= $p->getDateSubDaysExpression('test_datetime', 10) . ' AS sub_days, ';
579
        $sql .= $p->getDateAddWeeksExpression('test_datetime', 1) . ' AS add_weeks, ';
580
        $sql .= $p->getDateSubWeeksExpression('test_datetime', 1) . ' AS sub_weeks, ';
581
        $sql .= $p->getDateAddMonthExpression('test_datetime', 2) . ' AS add_month, ';
582
        $sql .= $p->getDateSubMonthExpression('test_datetime', 2) . ' AS sub_month, ';
583
        $sql .= $p->getDateAddQuartersExpression('test_datetime', 3) . ' AS add_quarters, ';
584
        $sql .= $p->getDateSubQuartersExpression('test_datetime', 3) . ' AS sub_quarters, ';
585
        $sql .= $p->getDateAddYearsExpression('test_datetime', 6) . ' AS add_years, ';
586
        $sql .= $p->getDateSubYearsExpression('test_datetime', 6) . ' AS sub_years ';
587
        $sql .= 'FROM fetch_table';
588
589
        $row = $this->connection->fetchAssoc($sql);
590
        $row = array_change_key_case($row, CASE_LOWER);
591
592
        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');
593
        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');
594
        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');
595
        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');
596
        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');
597
        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');
598
        self::assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), 'Adding date should end up on 2010-01-11');
599
        self::assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), 'Subtracting date should end up on 2009-12-22');
600
        self::assertEquals('2010-01-08', date('Y-m-d', strtotime($row['add_weeks'])), 'Adding week should end up on 2010-01-08');
601
        self::assertEquals('2009-12-25', date('Y-m-d', strtotime($row['sub_weeks'])), 'Subtracting week should end up on 2009-12-25');
602
        self::assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), 'Adding month should end up on 2010-03-01');
603
        self::assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), 'Subtracting month should end up on 2009-11-01');
604
        self::assertEquals('2010-10-01', date('Y-m-d', strtotime($row['add_quarters'])), 'Adding quarters should end up on 2010-04-01');
605
        self::assertEquals('2009-04-01', date('Y-m-d', strtotime($row['sub_quarters'])), 'Subtracting quarters should end up on 2009-10-01');
606
        self::assertEquals('2016-01-01', date('Y-m-d', strtotime($row['add_years'])), 'Adding years should end up on 2016-01-01');
607
        self::assertEquals('2004-01-01', date('Y-m-d', strtotime($row['sub_years'])), 'Subtracting years should end up on 2004-01-01');
608
    }
609
610
    public function testSqliteDateArithmeticWithDynamicInterval() : void
611
    {
612
        $platform = $this->connection->getDatabasePlatform();
613
614
        if (! $platform instanceof SqlitePlatform) {
615
            self::markTestSkipped('test is for sqlite only');
616
        }
617
618
        $table = new Table('fetch_table_date_math');
619
        $table->addColumn('test_date', 'date');
620
        $table->addColumn('test_days', 'integer');
621
        $table->setPrimaryKey(['test_date']);
622
623
        $sm = $this->connection->getSchemaManager();
624
        $sm->createTable($table);
625
626
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-01-01', 'test_days' => 10]);
627
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-06-01', 'test_days' => 20]);
628
629
        $sql  = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
630
        $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

630
        $sql .= $platform->getDateSubDaysExpression('test_date', /** @scrutinizer ignore-type */ 'test_days') . " < '2010-05-12'";
Loading history...
631
632
        $rowCount = $this->connection->fetchColumn($sql);
633
634
        self::assertEquals(1, $rowCount);
635
    }
636
637
    public function testLocateExpression() : void
638
    {
639
        $platform = $this->connection->getDatabasePlatform();
640
641
        $sql  = 'SELECT ';
642
        $sql .= $platform->getLocateExpression('test_string', "'oo'") . ' AS locate1, ';
643
        $sql .= $platform->getLocateExpression('test_string', "'foo'") . ' AS locate2, ';
644
        $sql .= $platform->getLocateExpression('test_string', "'bar'") . ' AS locate3, ';
645
        $sql .= $platform->getLocateExpression('test_string', 'test_string') . ' AS locate4, ';
646
        $sql .= $platform->getLocateExpression("'foo'", 'test_string') . ' AS locate5, ';
647
        $sql .= $platform->getLocateExpression("'barfoobaz'", 'test_string') . ' AS locate6, ';
648
        $sql .= $platform->getLocateExpression("'bar'", 'test_string') . ' AS locate7, ';
649
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 2) . ' AS locate8, ';
650
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 3) . ' AS locate9 ';
651
        $sql .= 'FROM fetch_table';
652
653
        $row = $this->connection->fetchAssoc($sql);
654
        $row = array_change_key_case($row, CASE_LOWER);
655
656
        self::assertEquals(2, $row['locate1']);
657
        self::assertEquals(1, $row['locate2']);
658
        self::assertEquals(0, $row['locate3']);
659
        self::assertEquals(1, $row['locate4']);
660
        self::assertEquals(1, $row['locate5']);
661
        self::assertEquals(4, $row['locate6']);
662
        self::assertEquals(0, $row['locate7']);
663
        self::assertEquals(2, $row['locate8']);
664
        self::assertEquals(0, $row['locate9']);
665
    }
666
667
    public function testQuoteSQLInjection() : void
668
    {
669
        $sql  = 'SELECT * FROM fetch_table WHERE test_string = ' . $this->connection->quote("bar' OR '1'='1");
670
        $rows = $this->connection->fetchAll($sql);
671
672
        self::assertCount(0, $rows, 'no result should be returned, otherwise SQL injection is possible');
673
    }
674
675
    /**
676
     * @group DDC-1213
677
     */
678
    public function testBitComparisonExpressionSupport() : void
679
    {
680
        $this->connection->exec('DELETE FROM fetch_table');
681
        $platform = $this->connection->getDatabasePlatform();
682
        $bitmap   = [];
683
684
        for ($i = 2; $i < 9; $i += 2) {
685
            $bitmap[$i] = [
686
                'bit_or'    => ($i | 2),
687
                'bit_and'   => ($i & 2),
688
            ];
689
            $this->connection->insert('fetch_table', [
690
                'test_int'      => $i,
691
                'test_string'   => json_encode($bitmap[$i]),
692
                'test_datetime' => '2010-01-01 10:10:10',
693
            ]);
694
        }
695
696
        $sql = 'SELECT test_int, test_string'
697
            . ', ' . $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or'
698
            . ', ' . $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and'
699
            . ' FROM fetch_table';
700
701
        $stmt = $this->connection->executeQuery($sql);
702
        $data = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
703
704
        self::assertCount(4, $data);
705
        self::assertEquals(count($bitmap), count($data));
706
        foreach ($data as $row) {
707
            $row = array_change_key_case($row, CASE_LOWER);
708
709
            self::assertArrayHasKey('test_int', $row);
710
711
            $id = $row['test_int'];
712
713
            self::assertArrayHasKey($id, $bitmap);
714
            self::assertArrayHasKey($id, $bitmap);
715
716
            self::assertArrayHasKey('bit_or', $row);
717
            self::assertArrayHasKey('bit_and', $row);
718
719
            self::assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
720
            self::assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
721
        }
722
    }
723
724
    public function testSetDefaultFetchMode() : void
725
    {
726
        $stmt = $this->connection->query('SELECT * FROM fetch_table');
727
        $stmt->setFetchMode(FetchMode::NUMERIC);
728
729
        $row = array_keys($stmt->fetch());
730
        self::assertCount(0, array_filter($row, static function ($v) : bool {
731
            return ! is_numeric($v);
732
        }), 'should be no non-numerical elements in the result.');
733
    }
734
735
    /**
736
     * @group DBAL-241
737
     */
738
    public function testFetchAllStyleColumn() : void
739
    {
740
        $sql = 'DELETE FROM fetch_table';
741
        $this->connection->executeUpdate($sql);
742
743
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo']);
744
        $this->connection->insert('fetch_table', ['test_int' => 10, 'test_string' => 'foo']);
745
746
        $sql  = 'SELECT test_int FROM fetch_table';
747
        $rows = $this->connection->query($sql)->fetchAll(FetchMode::COLUMN);
748
749
        self::assertEquals([1, 10], $rows);
750
    }
751
752
    /**
753
     * @group DBAL-257
754
     */
755
    public function testEmptyFetchColumnReturnsFalse() : void
756
    {
757
        $this->connection->beginTransaction();
758
        $this->connection->exec('DELETE FROM fetch_table');
759
        self::assertFalse($this->connection->fetchColumn('SELECT test_int FROM fetch_table'));
760
        self::assertFalse($this->connection->query('SELECT test_int FROM fetch_table')->fetchColumn());
761
        $this->connection->rollBack();
762
    }
763
764
    /**
765
     * @group DBAL-339
766
     */
767
    public function testSetFetchModeOnDbalStatement() : void
768
    {
769
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
770
        $stmt = $this->connection->executeQuery($sql, [1, 'foo']);
771
        $stmt->setFetchMode(FetchMode::NUMERIC);
772
773
        $row = $stmt->fetch();
774
775
        self::assertArrayHasKey(0, $row);
776
        self::assertArrayHasKey(1, $row);
777
        self::assertFalse($stmt->fetch());
778
    }
779
780
    /**
781
     * @group DBAL-435
782
     */
783
    public function testEmptyParameters() : void
784
    {
785
        $sql  = 'SELECT * FROM fetch_table WHERE test_int IN (?)';
786
        $stmt = $this->connection->executeQuery($sql, [[]], [Connection::PARAM_INT_ARRAY]);
787
        $rows = $stmt->fetchAll();
788
789
        self::assertEquals([], $rows);
790
    }
791
792
    /**
793
     * @group DBAL-1028
794
     */
795
    public function testFetchColumnNoResult() : void
796
    {
797
        self::assertFalse(
798
            $this->connection->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])
799
        );
800
    }
801
}
802