Failed Conditions
Push — master ( 01c22b...e42c1f )
by Marco
79:13 queued 10s
created

DataAccessTest::substringExpressionProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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

298
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
299
300
        self::assertEquals(1, $row['test_int']);
301
        self::assertEquals('foo', $row['test_string']);
302
    }
303
304
    public function testFetchAssocWithTypes() : void
305
    {
306
        $datetimeString = '2010-01-01 10:10:10';
307
        $datetime       = new DateTime($datetimeString);
308
309
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
310
        $row = $this->connection->fetchAssoc(
311
            $sql,
312
            [1, $datetime],
313
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
314
        );
315
316
        self::assertNotFalse($row);
317
318
        $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

318
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
319
320
        self::assertEquals(1, $row['test_int']);
321
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
322
    }
323
324
    public function testFetchAssocWithMissingTypes() : void
325
    {
326
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
327
            $this->connection->getDriver() instanceof SQLSrvDriver) {
328
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
329
        }
330
331
        $datetimeString = '2010-01-01 10:10:10';
332
        $datetime       = new DateTime($datetimeString);
333
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
334
335
        $this->expectException(DBALException::class);
336
337
        $this->connection->fetchAssoc($sql, [1, $datetime]);
338
    }
339
340
    public function testFetchArray() : void
341
    {
342
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
343
        $row = $this->connection->fetchArray($sql, [1, 'foo']);
344
345
        self::assertEquals(1, $row[0]);
346
        self::assertEquals('foo', $row[1]);
347
    }
348
349
    public function testFetchArrayWithTypes() : void
350
    {
351
        $datetimeString = '2010-01-01 10:10:10';
352
        $datetime       = new DateTime($datetimeString);
353
354
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
355
        $row = $this->connection->fetchArray(
356
            $sql,
357
            [1, $datetime],
358
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
359
        );
360
361
        self::assertNotFalse($row);
362
363
        $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

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

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

528
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
529
530
        self::assertEquals($expectedResult, $row['trimmed']);
531
    }
532
533
    /**
534
     * @return array<int, array<int, mixed>>
535
     */
536
    public static function getTrimExpressionData() : iterable
537
    {
538
        return [
539
            ['test_string', TrimMode::UNSPECIFIED, null, 'foo'],
540
            ['test_string', TrimMode::LEADING, null, 'foo'],
541
            ['test_string', TrimMode::TRAILING, null, 'foo'],
542
            ['test_string', TrimMode::BOTH, null, 'foo'],
543
            ['test_string', TrimMode::UNSPECIFIED, "'f'", 'oo'],
544
            ['test_string', TrimMode::UNSPECIFIED, "'o'", 'f'],
545
            ['test_string', TrimMode::UNSPECIFIED, "'.'", 'foo'],
546
            ['test_string', TrimMode::LEADING, "'f'", 'oo'],
547
            ['test_string', TrimMode::LEADING, "'o'", 'foo'],
548
            ['test_string', TrimMode::LEADING, "'.'", 'foo'],
549
            ['test_string', TrimMode::TRAILING, "'f'", 'foo'],
550
            ['test_string', TrimMode::TRAILING, "'o'", 'f'],
551
            ['test_string', TrimMode::TRAILING, "'.'", 'foo'],
552
            ['test_string', TrimMode::BOTH, "'f'", 'oo'],
553
            ['test_string', TrimMode::BOTH, "'o'", 'f'],
554
            ['test_string', TrimMode::BOTH, "'.'", 'foo'],
555
            ["' foo '", TrimMode::UNSPECIFIED, null, 'foo'],
556
            ["' foo '", TrimMode::LEADING, null, 'foo '],
557
            ["' foo '", TrimMode::TRAILING, null, ' foo'],
558
            ["' foo '", TrimMode::BOTH, null, 'foo'],
559
            ["' foo '", TrimMode::UNSPECIFIED, "'f'", ' foo '],
560
            ["' foo '", TrimMode::UNSPECIFIED, "'o'", ' foo '],
561
            ["' foo '", TrimMode::UNSPECIFIED, "'.'", ' foo '],
562
            ["' foo '", TrimMode::UNSPECIFIED, "' '", 'foo'],
563
            ["' foo '", TrimMode::LEADING, "'f'", ' foo '],
564
            ["' foo '", TrimMode::LEADING, "'o'", ' foo '],
565
            ["' foo '", TrimMode::LEADING, "'.'", ' foo '],
566
            ["' foo '", TrimMode::LEADING, "' '", 'foo '],
567
            ["' foo '", TrimMode::TRAILING, "'f'", ' foo '],
568
            ["' foo '", TrimMode::TRAILING, "'o'", ' foo '],
569
            ["' foo '", TrimMode::TRAILING, "'.'", ' foo '],
570
            ["' foo '", TrimMode::TRAILING, "' '", ' foo'],
571
            ["' foo '", TrimMode::BOTH, "'f'", ' foo '],
572
            ["' foo '", TrimMode::BOTH, "'o'", ' foo '],
573
            ["' foo '", TrimMode::BOTH, "'.'", ' foo '],
574
            ["' foo '", TrimMode::BOTH, "' '", 'foo'],
575
        ];
576
    }
577
578
    public function testTrimExpressionInvalidMode() : void
579
    {
580
        $this->expectException(InvalidArgumentException::class);
581
        $this->connection->getDatabasePlatform()->getTrimExpression('Trim me!', 0xBEEF);
582
    }
583
584
    /**
585
     * @dataProvider modeProvider
586
     */
587
    public function testDateAddSeconds(callable $buildQuery, callable $bindParams) : void
588
    {
589
        $this->assertDateExpression(
590
            $buildQuery,
591
            $bindParams,
592
            static function (AbstractPlatform $platform, string $interval) : string {
593
                return $platform->getDateAddSecondsExpression('test_datetime', $interval);
594
            },
595
            1,
596
            '2010-01-01 10:10:11'
597
        );
598
    }
599
600
    /**
601
     * @dataProvider modeProvider
602
     */
603
    public function testDateSubSeconds(callable $buildQuery, callable $bindParams) : void
604
    {
605
        $this->assertDateExpression(
606
            $buildQuery,
607
            $bindParams,
608
            static function (AbstractPlatform $platform, string $interval) : string {
609
                return $platform->getDateSubSecondsExpression('test_datetime', $interval);
610
            },
611
            1,
612
            '2010-01-01 10:10:09'
613
        );
614
    }
615
616
    /**
617
     * @dataProvider modeProvider
618
     */
619
    public function testDateAddMinutes(callable $buildQuery, callable $bindParams) : void
620
    {
621
        $this->assertDateExpression(
622
            $buildQuery,
623
            $bindParams,
624
            static function (AbstractPlatform $platform, string $interval) : string {
625
                return $platform->getDateAddMinutesExpression('test_datetime', $interval);
626
            },
627
            5,
628
            '2010-01-01 10:15:10'
629
        );
630
    }
631
632
    /**
633
     * @dataProvider modeProvider
634
     */
635
    public function testDateSubMinutes(callable $buildQuery, callable $bindParams) : void
636
    {
637
        $this->assertDateExpression(
638
            $buildQuery,
639
            $bindParams,
640
            static function (AbstractPlatform $platform, string $interval) : string {
641
                return $platform->getDateSubMinutesExpression('test_datetime', $interval);
642
            },
643
            5,
644
            '2010-01-01 10:05:10'
645
        );
646
    }
647
648
    /**
649
     * @dataProvider modeProvider
650
     */
651
    public function testDateAddHours(callable $buildQuery, callable $bindParams) : void
652
    {
653
        $this->assertDateExpression(
654
            $buildQuery,
655
            $bindParams,
656
            static function (AbstractPlatform $platform, string $interval) : string {
657
                return $platform->getDateAddHourExpression('test_datetime', $interval);
658
            },
659
            3,
660
            '2010-01-01 13:10:10'
661
        );
662
    }
663
664
    /**
665
     * @dataProvider modeProvider
666
     */
667
    public function testDateSubHours(callable $buildQuery, callable $bindParams) : void
668
    {
669
        $this->assertDateExpression(
670
            $buildQuery,
671
            $bindParams,
672
            static function (AbstractPlatform $platform, string $interval) : string {
673
                return $platform->getDateSubHourExpression('test_datetime', $interval);
674
            },
675
            3,
676
            '2010-01-01 07:10:10'
677
        );
678
    }
679
680
    /**
681
     * @dataProvider modeProvider
682
     */
683
    public function testDateAddDays(callable $buildQuery, callable $bindParams) : void
684
    {
685
        $this->assertDateExpression(
686
            $buildQuery,
687
            $bindParams,
688
            static function (AbstractPlatform $platform, string $interval) : string {
689
                return $platform->getDateAddDaysExpression('test_datetime', $interval);
690
            },
691
            10,
692
            '2010-01-11 10:10:10'
693
        );
694
    }
695
696
    /**
697
     * @dataProvider modeProvider
698
     */
699
    public function testDateSubDays(callable $buildQuery, callable $bindParams) : void
700
    {
701
        $this->assertDateExpression(
702
            $buildQuery,
703
            $bindParams,
704
            static function (AbstractPlatform $platform, string $interval) : string {
705
                return $platform->getDateSubDaysExpression('test_datetime', $interval);
706
            },
707
            10,
708
            '2009-12-22 10:10:10'
709
        );
710
    }
711
712
    /**
713
     * @dataProvider modeProvider
714
     */
715
    public function testDateAddWeeks(callable $buildQuery, callable $bindParams) : void
716
    {
717
        $this->assertDateExpression(
718
            $buildQuery,
719
            $bindParams,
720
            static function (AbstractPlatform $platform, string $interval) : string {
721
                return $platform->getDateAddWeeksExpression('test_datetime', $interval);
722
            },
723
            1,
724
            '2010-01-08 10:10:10'
725
        );
726
    }
727
728
    /**
729
     * @dataProvider modeProvider
730
     */
731
    public function testDateSubWeeks(callable $buildQuery, callable $bindParams) : void
732
    {
733
        $this->assertDateExpression(
734
            $buildQuery,
735
            $bindParams,
736
            static function (AbstractPlatform $platform, string $interval) : string {
737
                return $platform->getDateSubWeeksExpression('test_datetime', $interval);
738
            },
739
            1,
740
            '2009-12-25 10:10:10'
741
        );
742
    }
743
744
    /**
745
     * @dataProvider modeProvider
746
     */
747
    public function testDateAddMonths(callable $buildQuery, callable $bindParams) : void
748
    {
749
        $this->assertDateExpression(
750
            $buildQuery,
751
            $bindParams,
752
            static function (AbstractPlatform $platform, string $interval) : string {
753
                return $platform->getDateAddMonthExpression('test_datetime', $interval);
754
            },
755
            2,
756
            '2010-03-01 10:10:10'
757
        );
758
    }
759
760
    /**
761
     * @dataProvider modeProvider
762
     */
763
    public function testDateSubMonths(callable $buildQuery, callable $bindParams) : void
764
    {
765
        $this->assertDateExpression(
766
            $buildQuery,
767
            $bindParams,
768
            static function (AbstractPlatform $platform, string $interval) : string {
769
                return $platform->getDateSubMonthExpression('test_datetime', $interval);
770
            },
771
            2,
772
            '2009-11-01 10:10:10'
773
        );
774
    }
775
776
    /**
777
     * @dataProvider modeProvider
778
     */
779
    public function testDateAddQuarters(callable $buildQuery, callable $bindParams) : void
780
    {
781
        $this->assertDateExpression(
782
            $buildQuery,
783
            $bindParams,
784
            static function (AbstractPlatform $platform, string $interval) : string {
785
                return $platform->getDateAddQuartersExpression('test_datetime', $interval);
786
            },
787
            3,
788
            '2010-10-01 10:10:10'
789
        );
790
    }
791
792
    /**
793
     * @dataProvider modeProvider
794
     */
795
    public function testDateSubQuarters(callable $buildQuery, callable $bindParams) : void
796
    {
797
        $this->assertDateExpression(
798
            $buildQuery,
799
            $bindParams,
800
            static function (AbstractPlatform $platform, string $interval) : string {
801
                return $platform->getDateSubQuartersExpression('test_datetime', $interval);
802
            },
803
            3,
804
            '2009-04-01 10:10:10'
805
        );
806
    }
807
808
    /**
809
     * @dataProvider modeProvider
810
     */
811
    public function testDateAddYears(callable $buildQuery, callable $bindParams) : void
812
    {
813
        $this->assertDateExpression(
814
            $buildQuery,
815
            $bindParams,
816
            static function (AbstractPlatform $platform, string $interval) : string {
817
                return $platform->getDateAddYearsExpression('test_datetime', $interval);
818
            },
819
            6,
820
            '2016-01-01 10:10:10'
821
        );
822
    }
823
824
    /**
825
     * @dataProvider modeProvider
826
     */
827
    public function testDateSubYears(callable $buildQuery, callable $bindParams) : void
828
    {
829
        $this->assertDateExpression(
830
            $buildQuery,
831
            $bindParams,
832
            static function (AbstractPlatform $platform, string $interval) : string {
833
                return $platform->getDateSubYearsExpression('test_datetime', $interval);
834
            },
835
            6,
836
            '2004-01-01 10:10:10'
837
        );
838
    }
839
840
    /**
841
     * @param callable $buildQuery Builds the portion of the query representing the interval value
842
     * @param callable $bindParams Binds the interval value to the statement
843
     * @param callable $expression Builds the platform-specific interval expression
844
     * @param int      $interval   Interval value
845
     * @param string   $expected   Expected value
846
     */
847
    private function assertDateExpression(callable $buildQuery, callable $bindParams, callable $expression, int $interval, string $expected) : void
848
    {
849
        $connection = $this->connection;
850
        $platform   = $connection->getDatabasePlatform();
851
852
        $query = sprintf('SELECT %s FROM fetch_table', $expression($platform, $buildQuery($interval)));
853
        $stmt  = $connection->prepare($query);
854
        $bindParams($stmt, $interval);
855
856
        $stmt->execute();
857
858
        $date = $stmt->fetchColumn();
859
860
        $this->assertEquals($expected, date('Y-m-d H:i:s', strtotime($date)));
0 ignored issues
show
Bug introduced by
It seems like $date can also be of type false; however, parameter $time of strtotime() 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

860
        $this->assertEquals($expected, date('Y-m-d H:i:s', strtotime(/** @scrutinizer ignore-type */ $date)));
Loading history...
861
    }
862
863
    /**
864
     * @return mixed[][]
865
     */
866
    public static function modeProvider() : array
867
    {
868
        return [
869
            'bind' => [
870
                static function (int $interval) : string {
0 ignored issues
show
Unused Code introduced by
The parameter $interval is not used and could be removed. ( Ignorable by Annotation )

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

870
                static function (/** @scrutinizer ignore-unused */ int $interval) : string {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
871
                    return '?';
872
                },
873
                static function (Statement $stmt, int $interval) : void {
874
                    $stmt->bindParam(1, $interval, ParameterType::INTEGER);
875
                },
876
            ],
877
            'literal' => [
878
                static function (int $interval) : string {
879
                    return sprintf('%d', $interval);
880
                },
881
                static function (Statement $stmt, int $interval) : void {
0 ignored issues
show
Unused Code introduced by
The parameter $interval is not used and could be removed. ( Ignorable by Annotation )

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

881
                static function (Statement $stmt, /** @scrutinizer ignore-unused */ int $interval) : void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $stmt is not used and could be removed. ( Ignorable by Annotation )

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

881
                static function (/** @scrutinizer ignore-unused */ Statement $stmt, int $interval) : void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
882
                },
883
            ],
884
            'expression' => [
885
                static function (int $interval) : string {
886
                    return sprintf('(0 + %d)', $interval);
887
                },
888
                static function (Statement $stmt, int $interval) : void {
0 ignored issues
show
Unused Code introduced by
The parameter $interval is not used and could be removed. ( Ignorable by Annotation )

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

888
                static function (Statement $stmt, /** @scrutinizer ignore-unused */ int $interval) : void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $stmt is not used and could be removed. ( Ignorable by Annotation )

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

888
                static function (/** @scrutinizer ignore-unused */ Statement $stmt, int $interval) : void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
889
                },
890
            ],
891
        ];
892
    }
893
894
    public function testSqliteDateArithmeticWithDynamicInterval() : void
895
    {
896
        $platform = $this->connection->getDatabasePlatform();
897
898
        if (! $platform instanceof SqlitePlatform) {
899
            $this->markTestSkipped('test is for sqlite only');
900
        }
901
902
        $table = new Table('fetch_table_date_math');
903
        $table->addColumn('test_date', 'date');
904
        $table->addColumn('test_days', 'integer');
905
        $table->setPrimaryKey(['test_date']);
906
907
        $sm = $this->connection->getSchemaManager();
908
        $sm->createTable($table);
909
910
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-01-01', 'test_days' => 10]);
911
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-06-01', 'test_days' => 20]);
912
913
        $sql  = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
914
        $sql .= $platform->getDateSubDaysExpression('test_date', 'test_days') . " < '2010-05-12'";
915
916
        $rowCount = $this->connection->fetchColumn($sql, [], 0);
917
918
        $this->assertEquals(1, $rowCount);
919
    }
920
921
    public function testLocateExpression() : void
922
    {
923
        $platform = $this->connection->getDatabasePlatform();
924
925
        $sql  = 'SELECT ';
926
        $sql .= $platform->getLocateExpression('test_string', "'oo'") . ' AS locate1, ';
927
        $sql .= $platform->getLocateExpression('test_string', "'foo'") . ' AS locate2, ';
928
        $sql .= $platform->getLocateExpression('test_string', "'bar'") . ' AS locate3, ';
929
        $sql .= $platform->getLocateExpression('test_string', 'test_string') . ' AS locate4, ';
930
        $sql .= $platform->getLocateExpression("'foo'", 'test_string') . ' AS locate5, ';
931
        $sql .= $platform->getLocateExpression("'barfoobaz'", 'test_string') . ' AS locate6, ';
932
        $sql .= $platform->getLocateExpression("'bar'", 'test_string') . ' AS locate7, ';
933
        $sql .= $platform->getLocateExpression('test_string', "'oo'", '2') . ' AS locate8, ';
934
        $sql .= $platform->getLocateExpression('test_string', "'oo'", '3') . ' AS locate9 ';
935
        $sql .= 'FROM fetch_table';
936
937
        $row = $this->connection->fetchAssoc($sql);
938
        assert(is_array($row));
939
940
        $row = array_change_key_case($row, CASE_LOWER);
941
942
        self::assertEquals(2, $row['locate1']);
943
        self::assertEquals(1, $row['locate2']);
944
        self::assertEquals(0, $row['locate3']);
945
        self::assertEquals(1, $row['locate4']);
946
        self::assertEquals(1, $row['locate5']);
947
        self::assertEquals(4, $row['locate6']);
948
        self::assertEquals(0, $row['locate7']);
949
        self::assertEquals(2, $row['locate8']);
950
        self::assertEquals(0, $row['locate9']);
951
    }
952
953
    /**
954
     * @dataProvider substringExpressionProvider
955
     */
956
    public function testSubstringExpression(string $string, string $start, ?string $length, string $expected) : void
957
    {
958
        $platform = $this->connection->getDatabasePlatform();
959
960
        $query = $platform->getDummySelectSQL(
961
            $platform->getSubstringExpression($string, $start, $length)
962
        );
963
964
        $this->assertEquals($expected, $this->connection->fetchColumn($query));
965
    }
966
967
    /**
968
     * @return mixed[][]
969
     */
970
    public static function substringExpressionProvider() : iterable
971
    {
972
        return [
973
            'start-no-length' => [
974
                "'abcdef'",
975
                '3',
976
                null,
977
                'cdef',
978
            ],
979
            'start-with-length' => [
980
                "'abcdef'",
981
                '2',
982
                '4',
983
                'bcde',
984
            ],
985
            'expressions' => [
986
                "'abcdef'",
987
                '1 + 1',
988
                '1 + 1',
989
                'bc',
990
            ],
991
        ];
992
    }
993
994
    public function testQuoteSQLInjection() : void
995
    {
996
        $sql  = 'SELECT * FROM fetch_table WHERE test_string = ' . $this->connection->quote("bar' OR '1'='1");
997
        $rows = $this->connection->fetchAll($sql);
998
999
        self::assertCount(0, $rows, 'no result should be returned, otherwise SQL injection is possible');
1000
    }
1001
1002
    /**
1003
     * @group DDC-1213
1004
     */
1005
    public function testBitComparisonExpressionSupport() : void
1006
    {
1007
        $this->connection->exec('DELETE FROM fetch_table');
1008
        $platform = $this->connection->getDatabasePlatform();
1009
        $bitmap   = [];
1010
1011
        for ($i = 2; $i < 9; $i += 2) {
1012
            $bitmap[$i] = [
1013
                'bit_or'    => ($i | 2),
1014
                'bit_and'   => ($i & 2),
1015
            ];
1016
            $this->connection->insert('fetch_table', [
1017
                'test_int'      => $i,
1018
                'test_string'   => json_encode($bitmap[$i]),
1019
                'test_datetime' => '2010-01-01 10:10:10',
1020
            ]);
1021
        }
1022
1023
        $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...
1024
        $sql[] = 'test_int, ';
1025
        $sql[] = 'test_string, ';
1026
        $sql[] = $platform->getBitOrComparisonExpression('test_int', '2') . ' AS bit_or, ';
1027
        $sql[] = $platform->getBitAndComparisonExpression('test_int', '2') . ' AS bit_and ';
1028
        $sql[] = 'FROM fetch_table';
1029
1030
        $stmt = $this->connection->executeQuery(implode(PHP_EOL, $sql));
1031
        $data = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
1032
1033
        self::assertCount(4, $data);
1034
        self::assertEquals(count($bitmap), count($data));
1035
        foreach ($data as $row) {
1036
            $row = array_change_key_case($row, CASE_LOWER);
1037
1038
            self::assertArrayHasKey('test_int', $row);
1039
1040
            $id = $row['test_int'];
1041
1042
            self::assertArrayHasKey($id, $bitmap);
1043
            self::assertArrayHasKey($id, $bitmap);
1044
1045
            self::assertArrayHasKey('bit_or', $row);
1046
            self::assertArrayHasKey('bit_and', $row);
1047
1048
            self::assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
1049
            self::assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
1050
        }
1051
    }
1052
1053
    public function testSetDefaultFetchMode() : void
1054
    {
1055
        $stmt = $this->connection->query('SELECT * FROM fetch_table');
1056
        $stmt->setFetchMode(FetchMode::NUMERIC);
1057
1058
        $row = array_keys($stmt->fetch());
1059
        self::assertCount(0, array_filter($row, static function ($v) {
1060
            return ! is_numeric($v);
1061
        }), 'should be no non-numerical elements in the result.');
1062
    }
1063
1064
    /**
1065
     * @group DBAL-1091
1066
     */
1067
    public function testFetchAllStyleObject() : void
1068
    {
1069
        $this->setupFixture();
1070
1071
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
1072
        $stmt = $this->connection->prepare($sql);
1073
1074
        $stmt->execute();
1075
1076
        $results = $stmt->fetchAll(FetchMode::STANDARD_OBJECT);
1077
1078
        self::assertCount(1, $results);
1079
        self::assertInstanceOf('stdClass', $results[0]);
1080
1081
        self::assertEquals(
1082
            1,
1083
            property_exists($results[0], 'test_int') ? $results[0]->test_int : $results[0]->TEST_INT
1084
        );
1085
        self::assertEquals(
1086
            'foo',
1087
            property_exists($results[0], 'test_string') ? $results[0]->test_string : $results[0]->TEST_STRING
1088
        );
1089
        self::assertStringStartsWith(
1090
            '2010-01-01 10:10:10',
1091
            property_exists($results[0], 'test_datetime') ? $results[0]->test_datetime : $results[0]->TEST_DATETIME
1092
        );
1093
    }
1094
1095
    /**
1096
     * @group DBAL-196
1097
     */
1098
    public function testFetchAllSupportFetchClass() : void
1099
    {
1100
        $this->beforeFetchClassTest();
1101
        $this->setupFixture();
1102
1103
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
1104
        $stmt = $this->connection->prepare($sql);
1105
        $stmt->execute();
1106
1107
        $results = $stmt->fetchAll(
1108
            FetchMode::CUSTOM_OBJECT,
1109
            MyFetchClass::class
1110
        );
1111
1112
        self::assertCount(1, $results);
1113
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
1114
1115
        self::assertEquals(1, $results[0]->test_int);
1116
        self::assertEquals('foo', $results[0]->test_string);
1117
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
1118
    }
1119
1120
    /**
1121
     * @group DBAL-241
1122
     */
1123
    public function testFetchAllStyleColumn() : void
1124
    {
1125
        $sql = 'DELETE FROM fetch_table';
1126
        $this->connection->executeUpdate($sql);
1127
1128
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo']);
1129
        $this->connection->insert('fetch_table', ['test_int' => 10, 'test_string' => 'foo']);
1130
1131
        $sql  = 'SELECT test_int FROM fetch_table';
1132
        $rows = $this->connection->query($sql)->fetchAll(FetchMode::COLUMN);
1133
1134
        self::assertEquals([1, 10], $rows);
1135
    }
1136
1137
    /**
1138
     * @group DBAL-214
1139
     */
1140
    public function testSetFetchModeClassFetchAll() : void
1141
    {
1142
        $this->beforeFetchClassTest();
1143
        $this->setupFixture();
1144
1145
        $sql  = 'SELECT * FROM fetch_table';
1146
        $stmt = $this->connection->query($sql);
1147
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
1148
1149
        $results = $stmt->fetchAll();
1150
1151
        self::assertCount(1, $results);
1152
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
1153
1154
        self::assertEquals(1, $results[0]->test_int);
1155
        self::assertEquals('foo', $results[0]->test_string);
1156
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
1157
    }
1158
1159
    /**
1160
     * @group DBAL-214
1161
     */
1162
    public function testSetFetchModeClassFetch() : void
1163
    {
1164
        $this->beforeFetchClassTest();
1165
        $this->setupFixture();
1166
1167
        $sql  = 'SELECT * FROM fetch_table';
1168
        $stmt = $this->connection->query($sql);
1169
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
1170
1171
        $results = [];
1172
        while ($row = $stmt->fetch()) {
1173
            $results[] = $row;
1174
        }
1175
1176
        self::assertCount(1, $results);
1177
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
1178
1179
        self::assertEquals(1, $results[0]->test_int);
1180
        self::assertEquals('foo', $results[0]->test_string);
1181
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
1182
    }
1183
1184
    /**
1185
     * @group DBAL-257
1186
     */
1187
    public function testEmptyFetchColumnReturnsFalse() : void
1188
    {
1189
        $this->connection->beginTransaction();
1190
        $this->connection->exec('DELETE FROM fetch_table');
1191
        self::assertFalse($this->connection->fetchColumn('SELECT test_int FROM fetch_table'));
1192
        self::assertFalse($this->connection->query('SELECT test_int FROM fetch_table')->fetchColumn());
1193
        $this->connection->rollBack();
1194
    }
1195
1196
    /**
1197
     * @group DBAL-339
1198
     */
1199
    public function testSetFetchModeOnDbalStatement() : void
1200
    {
1201
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
1202
        $stmt = $this->connection->executeQuery($sql, [1, 'foo']);
1203
        $stmt->setFetchMode(FetchMode::NUMERIC);
1204
1205
        $row = $stmt->fetch();
1206
1207
        self::assertArrayHasKey(0, $row);
1208
        self::assertArrayHasKey(1, $row);
1209
        self::assertFalse($stmt->fetch());
1210
    }
1211
1212
    /**
1213
     * @group DBAL-435
1214
     */
1215
    public function testEmptyParameters() : void
1216
    {
1217
        $sql  = 'SELECT * FROM fetch_table WHERE test_int IN (?)';
1218
        $stmt = $this->connection->executeQuery($sql, [[]], [Connection::PARAM_INT_ARRAY]);
1219
        $rows = $stmt->fetchAll();
1220
1221
        self::assertEquals([], $rows);
1222
    }
1223
1224
    /**
1225
     * @group DBAL-1028
1226
     */
1227
    public function testFetchColumnNullValue() : void
1228
    {
1229
        $this->connection->executeUpdate(
1230
            'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)',
1231
            [2, 'foo']
1232
        );
1233
1234
        self::assertNull(
1235
            $this->connection->fetchColumn('SELECT test_datetime FROM fetch_table WHERE test_int = ?', [2])
1236
        );
1237
    }
1238
1239
    /**
1240
     * @group DBAL-1028
1241
     */
1242
    public function testFetchColumnNoResult() : void
1243
    {
1244
        self::assertFalse(
1245
            $this->connection->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])
1246
        );
1247
    }
1248
1249
    private function setupFixture() : void
1250
    {
1251
        $this->connection->exec('DELETE FROM fetch_table');
1252
        $this->connection->insert('fetch_table', [
1253
            'test_int'      => 1,
1254
            'test_string'   => 'foo',
1255
            'test_datetime' => '2010-01-01 10:10:10',
1256
        ]);
1257
    }
1258
1259
    private function beforeFetchClassTest() : void
1260
    {
1261
        $driver = $this->connection->getDriver();
1262
1263
        if ($driver instanceof Oci8Driver) {
1264
            $this->markTestSkipped('Not supported by OCI8');
1265
        }
1266
1267
        if ($driver instanceof MySQLiDriver) {
1268
            $this->markTestSkipped('Mysqli driver dont support this feature.');
1269
        }
1270
1271
        if (! $driver instanceof PDOOracleDriver) {
1272
            return;
1273
        }
1274
1275
        /** @var PDOConnection $connection */
1276
        $connection = $this->connection
1277
            ->getWrappedConnection();
1278
        $connection->getWrappedConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
1279
    }
1280
}
1281
1282
class MyFetchClass
1283
{
1284
    /** @var int */
1285
    public $test_int;
1286
1287
    /** @var string */
1288
    public $test_string;
1289
1290
    /** @var string */
1291
    public $test_datetime;
1292
}
1293