Failed Conditions
Pull Request — master (#3359)
by Sergei
25:01 queued 22:04
created

DataAccessTest::testFetchColumnNonExistingIndex()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

338
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
339
340
        self::assertEquals(1, $row[0]);
341
        self::assertStringStartsWith($datetimeString, $row[1]);
342
    }
343
344
    /**
345
     * @expectedException \Doctrine\DBAL\DBALException
346
     */
347
    public function testFetchArrayWithMissingTypes()
348
    {
349
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
350
            $this->connection->getDriver() instanceof SQLSrvDriver) {
351
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
352
        }
353
354
        $datetimeString = '2010-01-01 10:10:10';
355
        $datetime       = new DateTime($datetimeString);
356
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
357
        $row            = $this->connection->fetchArray($sql, [1, $datetime]);
0 ignored issues
show
Unused Code introduced by
The assignment to $row is dead and can be removed.
Loading history...
358
    }
359
360
    public function testFetchColumn()
361
    {
362
        $sql     = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
363
        $testInt = $this->connection->fetchColumn($sql, [1, 'foo'], 0);
364
365
        self::assertEquals(1, $testInt);
366
367
        $sql        = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
368
        $testString = $this->connection->fetchColumn($sql, [1, 'foo'], 1);
369
370
        self::assertEquals('foo', $testString);
371
    }
372
373
    public function testFetchColumnWithTypes()
374
    {
375
        $datetimeString = '2010-01-01 10:10:10';
376
        $datetime       = new DateTime($datetimeString);
377
378
        $sql    = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
379
        $column = $this->connection->fetchColumn($sql, [1, $datetime], 1, [ParameterType::STRING, Type::DATETIME]);
380
381
        self::assertNotFalse($column);
382
383
        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

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

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

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

609
        $sql .= $platform->getDateSubDaysExpression('test_date', /** @scrutinizer ignore-type */ 'test_days') . " < '2010-05-12'";
Loading history...
610
611
        $rowCount = $this->connection->fetchColumn($sql, [], 0);
612
613
        $this->assertEquals(1, $rowCount);
614
    }
615
616
    public function testLocateExpression()
617
    {
618
        $platform = $this->connection->getDatabasePlatform();
619
620
        $sql  = 'SELECT ';
621
        $sql .= $platform->getLocateExpression('test_string', "'oo'") . ' AS locate1, ';
622
        $sql .= $platform->getLocateExpression('test_string', "'foo'") . ' AS locate2, ';
623
        $sql .= $platform->getLocateExpression('test_string', "'bar'") . ' AS locate3, ';
624
        $sql .= $platform->getLocateExpression('test_string', 'test_string') . ' AS locate4, ';
625
        $sql .= $platform->getLocateExpression("'foo'", 'test_string') . ' AS locate5, ';
626
        $sql .= $platform->getLocateExpression("'barfoobaz'", 'test_string') . ' AS locate6, ';
627
        $sql .= $platform->getLocateExpression("'bar'", 'test_string') . ' AS locate7, ';
628
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 2) . ' AS locate8, ';
629
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 3) . ' AS locate9 ';
630
        $sql .= 'FROM fetch_table';
631
632
        $row = $this->connection->fetchAssoc($sql);
633
        $row = array_change_key_case($row, CASE_LOWER);
634
635
        self::assertEquals(2, $row['locate1']);
636
        self::assertEquals(1, $row['locate2']);
637
        self::assertEquals(0, $row['locate3']);
638
        self::assertEquals(1, $row['locate4']);
639
        self::assertEquals(1, $row['locate5']);
640
        self::assertEquals(4, $row['locate6']);
641
        self::assertEquals(0, $row['locate7']);
642
        self::assertEquals(2, $row['locate8']);
643
        self::assertEquals(0, $row['locate9']);
644
    }
645
646
    public function testQuoteSQLInjection()
647
    {
648
        $sql  = 'SELECT * FROM fetch_table WHERE test_string = ' . $this->connection->quote("bar' OR '1'='1");
649
        $rows = $this->connection->fetchAll($sql);
650
651
        self::assertCount(0, $rows, 'no result should be returned, otherwise SQL injection is possible');
652
    }
653
654
    /**
655
     * @group DDC-1213
656
     */
657
    public function testBitComparisonExpressionSupport()
658
    {
659
        $this->connection->exec('DELETE FROM fetch_table');
660
        $platform = $this->connection->getDatabasePlatform();
661
        $bitmap   = [];
662
663
        for ($i = 2; $i < 9; $i += 2) {
664
            $bitmap[$i] = [
665
                'bit_or'    => ($i | 2),
666
                'bit_and'   => ($i & 2),
667
            ];
668
            $this->connection->insert('fetch_table', [
669
                'test_int'      => $i,
670
                'test_string'   => json_encode($bitmap[$i]),
671
                'test_datetime' => '2010-01-01 10:10:10',
672
            ]);
673
        }
674
675
        $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...
676
        $sql[] = 'test_int, ';
677
        $sql[] = 'test_string, ';
678
        $sql[] = $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or, ';
679
        $sql[] = $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and ';
680
        $sql[] = 'FROM fetch_table';
681
682
        $stmt = $this->connection->executeQuery(implode(PHP_EOL, $sql));
683
        $data = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
684
685
        self::assertCount(4, $data);
686
        self::assertEquals(count($bitmap), count($data));
687
        foreach ($data as $row) {
688
            $row = array_change_key_case($row, CASE_LOWER);
689
690
            self::assertArrayHasKey('test_int', $row);
691
692
            $id = $row['test_int'];
693
694
            self::assertArrayHasKey($id, $bitmap);
695
            self::assertArrayHasKey($id, $bitmap);
696
697
            self::assertArrayHasKey('bit_or', $row);
698
            self::assertArrayHasKey('bit_and', $row);
699
700
            self::assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
701
            self::assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
702
        }
703
    }
704
705
    public function testSetDefaultFetchMode()
706
    {
707
        $stmt = $this->connection->query('SELECT * FROM fetch_table');
708
        $stmt->setFetchMode(FetchMode::NUMERIC);
709
710
        $row = array_keys($stmt->fetch());
711
        self::assertCount(0, array_filter($row, static function ($v) {
712
            return ! is_numeric($v);
713
        }), 'should be no non-numerical elements in the result.');
714
    }
715
716
    /**
717
     * @group DBAL-1091
718
     */
719
    public function testFetchAllStyleObject()
720
    {
721
        $this->setupFixture();
722
723
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
724
        $stmt = $this->connection->prepare($sql);
725
726
        $stmt->execute();
727
728
        $results = $stmt->fetchAll(FetchMode::STANDARD_OBJECT);
729
730
        self::assertCount(1, $results);
731
        self::assertInstanceOf('stdClass', $results[0]);
732
733
        self::assertEquals(
734
            1,
735
            property_exists($results[0], 'test_int') ? $results[0]->test_int : $results[0]->TEST_INT
736
        );
737
        self::assertEquals(
738
            'foo',
739
            property_exists($results[0], 'test_string') ? $results[0]->test_string : $results[0]->TEST_STRING
740
        );
741
        self::assertStringStartsWith(
742
            '2010-01-01 10:10:10',
743
            property_exists($results[0], 'test_datetime') ? $results[0]->test_datetime : $results[0]->TEST_DATETIME
744
        );
745
    }
746
747
    /**
748
     * @group DBAL-196
749
     */
750
    public function testFetchAllSupportFetchClass()
751
    {
752
        $this->skipOci8AndMysqli();
753
        $this->setupFixture();
754
755
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
756
        $stmt = $this->connection->prepare($sql);
757
        $stmt->execute();
758
759
        $results = $stmt->fetchAll(
760
            FetchMode::CUSTOM_OBJECT,
761
            MyFetchClass::class
762
        );
763
764
        self::assertCount(1, $results);
765
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
766
767
        self::assertEquals(1, $results[0]->test_int);
768
        self::assertEquals('foo', $results[0]->test_string);
769
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
770
    }
771
772
    /**
773
     * @group DBAL-241
774
     */
775
    public function testFetchAllStyleColumn()
776
    {
777
        $sql = 'DELETE FROM fetch_table';
778
        $this->connection->executeUpdate($sql);
779
780
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo']);
781
        $this->connection->insert('fetch_table', ['test_int' => 10, 'test_string' => 'foo']);
782
783
        $sql  = 'SELECT test_int FROM fetch_table';
784
        $rows = $this->connection->query($sql)->fetchAll(FetchMode::COLUMN);
785
786
        self::assertEquals([1, 10], $rows);
787
    }
788
789
    /**
790
     * @group DBAL-214
791
     */
792
    public function testSetFetchModeClassFetchAll()
793
    {
794
        $this->skipOci8AndMysqli();
795
        $this->setupFixture();
796
797
        $sql  = 'SELECT * FROM fetch_table';
798
        $stmt = $this->connection->query($sql);
799
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
800
801
        $results = $stmt->fetchAll();
802
803
        self::assertCount(1, $results);
804
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
805
806
        self::assertEquals(1, $results[0]->test_int);
807
        self::assertEquals('foo', $results[0]->test_string);
808
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
809
    }
810
811
    /**
812
     * @group DBAL-214
813
     */
814
    public function testSetFetchModeClassFetch()
815
    {
816
        $this->skipOci8AndMysqli();
817
        $this->setupFixture();
818
819
        $sql  = 'SELECT * FROM fetch_table';
820
        $stmt = $this->connection->query($sql);
821
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
822
823
        $results = [];
824
        while ($row = $stmt->fetch()) {
825
            $results[] = $row;
826
        }
827
828
        self::assertCount(1, $results);
829
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
830
831
        self::assertEquals(1, $results[0]->test_int);
832
        self::assertEquals('foo', $results[0]->test_string);
833
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
834
    }
835
836
    /**
837
     * @group DBAL-257
838
     */
839
    public function testEmptyFetchColumnReturnsFalse()
840
    {
841
        $this->connection->beginTransaction();
842
        $this->connection->exec('DELETE FROM fetch_table');
843
        self::assertFalse($this->connection->fetchColumn('SELECT test_int FROM fetch_table'));
844
        self::assertFalse($this->connection->query('SELECT test_int FROM fetch_table')->fetchColumn());
845
        $this->connection->rollBack();
846
    }
847
848
    /**
849
     * @group DBAL-339
850
     */
851
    public function testSetFetchModeOnDbalStatement()
852
    {
853
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
854
        $stmt = $this->connection->executeQuery($sql, [1, 'foo']);
855
        $stmt->setFetchMode(FetchMode::NUMERIC);
856
857
        $row = $stmt->fetch();
858
859
        self::assertArrayHasKey(0, $row);
860
        self::assertArrayHasKey(1, $row);
861
        self::assertFalse($stmt->fetch());
862
    }
863
864
    /**
865
     * @group DBAL-435
866
     */
867
    public function testEmptyParameters()
868
    {
869
        $sql  = 'SELECT * FROM fetch_table WHERE test_int IN (?)';
870
        $stmt = $this->connection->executeQuery($sql, [[]], [Connection::PARAM_INT_ARRAY]);
871
        $rows = $stmt->fetchAll();
872
873
        self::assertEquals([], $rows);
874
    }
875
876
    /**
877
     * @group DBAL-1028
878
     */
879
    public function testFetchColumnNullValue()
880
    {
881
        $this->connection->executeUpdate(
882
            'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)',
883
            [2, 'foo']
884
        );
885
886
        self::assertNull(
887
            $this->connection->fetchColumn('SELECT test_datetime FROM fetch_table WHERE test_int = ?', [2])
888
        );
889
    }
890
891
    /**
892
     * @group DBAL-1028
893
     */
894
    public function testFetchColumnNoResult()
895
    {
896
        self::assertFalse(
897
            $this->connection->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])
898
        );
899
    }
900
901
    private function setupFixture()
902
    {
903
        $this->connection->exec('DELETE FROM fetch_table');
904
        $this->connection->insert('fetch_table', [
905
            'test_int'      => 1,
906
            'test_string'   => 'foo',
907
            'test_datetime' => '2010-01-01 10:10:10',
908
        ]);
909
    }
910
911
    private function skipOci8AndMysqli()
912
    {
913
        if (isset($GLOBALS['db_type']) && $GLOBALS['db_type'] === 'oci8') {
914
            $this->markTestSkipped('Not supported by OCI8');
915
        }
916
        if ($this->connection->getDriver()->getName() !== 'mysqli') {
917
            return;
918
        }
919
920
        $this->markTestSkipped('Mysqli driver dont support this feature.');
921
    }
922
}
923
924
class MyFetchClass
925
{
926
    /** @var int */
927
    public $test_int;
928
929
    /** @var string */
930
    public $test_string;
931
932
    /** @var string */
933
    public $test_datetime;
934
}
935