Failed Conditions
Pull Request — master (#3339)
by Sergei
10:43
created

DataAccessTest::skipUnsupportedDrivers()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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