Completed
Push — develop ( 8dba78...8cb2fb )
by Marco
31s queued 13s
created

DataAccessTest::assertDateExpression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

290
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
291
292
        self::assertEquals(1, $row['test_int']);
293
        self::assertEquals('foo', $row['test_string']);
294
    }
295
296
    public function testFetchAssocWithTypes()
297
    {
298
        $datetimeString = '2010-01-01 10:10:10';
299
        $datetime       = new DateTime($datetimeString);
300
301
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
302
        $row = $this->connection->fetchAssoc($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
303
304
        self::assertNotFalse($row);
305
306
        $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

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

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

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

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

834
        $this->assertEquals($expected, date('Y-m-d H:i:s', strtotime(/** @scrutinizer ignore-type */ $date)));
Loading history...
835
    }
836
837
    /**
838
     * @return mixed[][]
839
     */
840
    public static function modeProvider() : array
841
    {
842
        return [
843
            'bind' => [
844
                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

844
                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...
845
                    return '?';
846
                },
847
                static function (Statement $stmt, int $interval) : void {
848
                    $stmt->bindParam(1, $interval, ParameterType::INTEGER);
849
                },
850
            ],
851
            'literal' => [
852
                static function (int $interval) : string {
853
                    return sprintf('%d', $interval);
854
                },
855
                static function (Statement $stmt, int $interval) : void {
0 ignored issues
show
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

855
                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...
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

855
                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...
856
                },
857
            ],
858
            'expression' => [
859
                static function (int $interval) : string {
860
                    return sprintf('(0 + %d)', $interval);
861
                },
862
                static function (Statement $stmt, int $interval) : void {
0 ignored issues
show
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

862
                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...
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

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