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

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

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

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

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

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

853
        $this->assertEquals($expected, date('Y-m-d H:i:s', strtotime(/** @scrutinizer ignore-type */ $date)));
Loading history...
854
    }
855
856
    /**
857
     * @return mixed[][]
858
     */
859
    public static function modeProvider() : array
860
    {
861
        return [
862
            'bind' => [
863
                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

863
                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...
864
                    return '?';
865
                },
866
                static function (Statement $stmt, int $interval) : void {
867
                    $stmt->bindParam(1, $interval, ParameterType::INTEGER);
868
                },
869
            ],
870
            'literal' => [
871
                static function (int $interval) : string {
872
                    return sprintf('%d', $interval);
873
                },
874
                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

874
                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

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