Completed
Pull Request — master (#3339)
by Sergei
85:57 queued 24:43
created

DataAccessTest::setupFixture()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
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\PDOConnection;
10
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
11
use Doctrine\DBAL\Driver\PDOSqlsrv\Driver as PDOSQLSRVDriver;
12
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
13
use Doctrine\DBAL\FetchMode;
14
use Doctrine\DBAL\ParameterType;
15
use Doctrine\DBAL\Platforms\SqlitePlatform;
16
use Doctrine\DBAL\Platforms\TrimMode;
17
use Doctrine\DBAL\Schema\Table;
18
use Doctrine\DBAL\Statement;
19
use Doctrine\DBAL\Types\Type;
20
use Doctrine\Tests\DbalFunctionalTestCase;
21
use PDO;
22
use const CASE_LOWER;
23
use const PHP_EOL;
24
use function array_change_key_case;
25
use function array_filter;
26
use function array_keys;
27
use function count;
28
use function date;
29
use function implode;
30
use function is_numeric;
31
use function json_encode;
32
use function property_exists;
33
use function sprintf;
34
use function strtotime;
35
36
class DataAccessTest extends DbalFunctionalTestCase
37
{
38
    /** @var bool */
39
    static private $generated = false;
40
41
    protected function setUp()
42
    {
43
        parent::setUp();
44
45
        if (self::$generated !== false) {
46
            return;
47
        }
48
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
        $sm = $this->connection->getSchemaManager();
608
        $sm->createTable($table);
609
610
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-01-01', 'test_days' => 10]);
611
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-06-01', 'test_days' => 20]);
612
613
        $sql  = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
614
        $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

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