Completed
Push — master ( 9b88bf...94cec7 )
by Marco
31s queued 15s
created

Doctrine/Tests/DBAL/Functional/DataAccessTest.php (1 issue)

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\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\Table;
17
use Doctrine\DBAL\Statement;
18
use Doctrine\DBAL\Types\Type;
19
use Doctrine\Tests\DbalFunctionalTestCase;
20
use PDO;
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
        $table = new Table('fetch_table');
49
        $table->addColumn('test_int', 'integer');
50
        $table->addColumn('test_string', 'string');
51
        $table->addColumn('test_datetime', 'datetime', ['notnull' => false]);
52
        $table->setPrimaryKey(['test_int']);
53
54
        $sm = $this->connection->getSchemaManager();
55
        $sm->createTable($table);
56
57
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo', 'test_datetime' => '2010-01-01 10:10:10']);
58
        self::$generated = true;
59
    }
60
61
    public function testPrepareWithBindValue()
62
    {
63
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
64
        $stmt = $this->connection->prepare($sql);
65
        self::assertInstanceOf(Statement::class, $stmt);
66
67
        $stmt->bindValue(1, 1);
68
        $stmt->bindValue(2, 'foo');
69
        $stmt->execute();
70
71
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
72
        $row = array_change_key_case($row, CASE_LOWER);
73
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
74
    }
75
76
    public function testPrepareWithBindParam()
77
    {
78
        $paramInt = 1;
79
        $paramStr = 'foo';
80
81
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
82
        $stmt = $this->connection->prepare($sql);
83
        self::assertInstanceOf(Statement::class, $stmt);
84
85
        $stmt->bindParam(1, $paramInt);
86
        $stmt->bindParam(2, $paramStr);
87
        $stmt->execute();
88
89
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
90
        $row = array_change_key_case($row, CASE_LOWER);
91
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
92
    }
93
94
    public function testPrepareWithFetchAll()
95
    {
96
        $paramInt = 1;
97
        $paramStr = 'foo';
98
99
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
100
        $stmt = $this->connection->prepare($sql);
101
        self::assertInstanceOf(Statement::class, $stmt);
102
103
        $stmt->bindParam(1, $paramInt);
104
        $stmt->bindParam(2, $paramStr);
105
        $stmt->execute();
106
107
        $rows    = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
108
        $rows[0] = array_change_key_case($rows[0], CASE_LOWER);
109
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
110
    }
111
112
    /**
113
     * @group DBAL-228
114
     */
115
    public function testPrepareWithFetchAllBoth()
116
    {
117
        $paramInt = 1;
118
        $paramStr = 'foo';
119
120
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
121
        $stmt = $this->connection->prepare($sql);
122
        self::assertInstanceOf(Statement::class, $stmt);
123
124
        $stmt->bindParam(1, $paramInt);
125
        $stmt->bindParam(2, $paramStr);
126
        $stmt->execute();
127
128
        $rows    = $stmt->fetchAll(FetchMode::MIXED);
129
        $rows[0] = array_change_key_case($rows[0], CASE_LOWER);
130
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo', 0 => 1, 1 => 'foo'], $rows[0]);
131
    }
132
133
    public function testPrepareWithFetchColumn()
134
    {
135
        $paramInt = 1;
136
        $paramStr = 'foo';
137
138
        $sql  = 'SELECT test_int FROM fetch_table WHERE test_int = ? AND test_string = ?';
139
        $stmt = $this->connection->prepare($sql);
140
        self::assertInstanceOf(Statement::class, $stmt);
141
142
        $stmt->bindParam(1, $paramInt);
143
        $stmt->bindParam(2, $paramStr);
144
        $stmt->execute();
145
146
        $column = $stmt->fetchColumn();
147
        self::assertEquals(1, $column);
148
    }
149
150
    public function testPrepareWithIterator()
151
    {
152
        $paramInt = 1;
153
        $paramStr = 'foo';
154
155
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
156
        $stmt = $this->connection->prepare($sql);
157
        self::assertInstanceOf(Statement::class, $stmt);
158
159
        $stmt->bindParam(1, $paramInt);
160
        $stmt->bindParam(2, $paramStr);
161
        $stmt->execute();
162
163
        $rows = [];
164
        $stmt->setFetchMode(FetchMode::ASSOCIATIVE);
165
        foreach ($stmt as $row) {
166
            $rows[] = array_change_key_case($row, CASE_LOWER);
167
        }
168
169
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
170
    }
171
172
    public function testPrepareWithQuoted()
173
    {
174
        $table    = 'fetch_table';
175
        $paramInt = 1;
176
        $paramStr = 'foo';
177
178
        $stmt = $this->connection->prepare(sprintf(
179
            'SELECT test_int, test_string FROM %s WHERE test_int = %s AND test_string = %s',
180
            $this->connection->quoteIdentifier($table),
181
            $this->connection->quote($paramInt),
182
            $this->connection->quote($paramStr)
183
        ));
184
        self::assertInstanceOf(Statement::class, $stmt);
185
    }
186
187
    public function testPrepareWithExecuteParams()
188
    {
189
        $paramInt = 1;
190
        $paramStr = 'foo';
191
192
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
193
        $stmt = $this->connection->prepare($sql);
194
        self::assertInstanceOf(Statement::class, $stmt);
195
        $stmt->execute([$paramInt, $paramStr]);
196
197
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
198
        self::assertNotFalse($row);
199
        $row = array_change_key_case($row, CASE_LOWER);
200
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
201
    }
202
203
    public function testFetchAll()
204
    {
205
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
206
        $data = $this->connection->fetchAll($sql, [1, 'foo']);
207
208
        self::assertCount(1, $data);
209
210
        $row = $data[0];
211
        self::assertCount(2, $row);
212
213
        $row = array_change_key_case($row, CASE_LOWER);
214
        self::assertEquals(1, $row['test_int']);
215
        self::assertEquals('foo', $row['test_string']);
216
    }
217
218
    /**
219
     * @group DBAL-209
220
     */
221
    public function testFetchAllWithTypes()
222
    {
223
        $datetimeString = '2010-01-01 10:10:10';
224
        $datetime       = new DateTime($datetimeString);
225
226
        $sql  = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
227
        $data = $this->connection->fetchAll($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
228
229
        self::assertCount(1, $data);
230
231
        $row = $data[0];
232
        self::assertCount(2, $row);
233
234
        $row = array_change_key_case($row, CASE_LOWER);
235
        self::assertEquals(1, $row['test_int']);
236
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
237
    }
238
239
    /**
240
     * @group DBAL-209
241
     * @expectedException \Doctrine\DBAL\DBALException
242
     */
243
    public function testFetchAllWithMissingTypes()
244
    {
245
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
246
            $this->connection->getDriver() instanceof SQLSrvDriver) {
247
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
248
        }
249
250
        $datetimeString = '2010-01-01 10:10:10';
251
        $datetime       = new DateTime($datetimeString);
252
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
253
        $this->connection->fetchAll($sql, [1, $datetime]);
254
    }
255
256
    public function testFetchBoth()
257
    {
258
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
259
        $row = $this->connection->executeQuery($sql, [1, 'foo'])->fetch(FetchMode::MIXED);
260
261
        self::assertNotFalse($row);
262
263
        $row = array_change_key_case($row, CASE_LOWER);
264
265
        self::assertEquals(1, $row['test_int']);
266
        self::assertEquals('foo', $row['test_string']);
267
        self::assertEquals(1, $row[0]);
268
        self::assertEquals('foo', $row[1]);
269
    }
270
271
    public function testFetchNoResult()
272
    {
273
        self::assertFalse(
274
            $this->connection->executeQuery('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])->fetch()
275
        );
276
    }
277
278
    public function testFetchAssoc()
279
    {
280
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
281
        $row = $this->connection->fetchAssoc($sql, [1, 'foo']);
282
283
        self::assertNotFalse($row);
284
285
        $row = array_change_key_case($row, CASE_LOWER);
286
287
        self::assertEquals(1, $row['test_int']);
288
        self::assertEquals('foo', $row['test_string']);
289
    }
290
291
    public function testFetchAssocWithTypes()
292
    {
293
        $datetimeString = '2010-01-01 10:10:10';
294
        $datetime       = new DateTime($datetimeString);
295
296
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
297
        $row = $this->connection->fetchAssoc($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
298
299
        self::assertNotFalse($row);
300
301
        $row = array_change_key_case($row, CASE_LOWER);
302
303
        self::assertEquals(1, $row['test_int']);
304
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
305
    }
306
307
    /**
308
     * @expectedException \Doctrine\DBAL\DBALException
309
     */
310
    public function testFetchAssocWithMissingTypes()
311
    {
312
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
313
            $this->connection->getDriver() instanceof SQLSrvDriver) {
314
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
315
        }
316
317
        $datetimeString = '2010-01-01 10:10:10';
318
        $datetime       = new DateTime($datetimeString);
319
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
320
        $this->connection->fetchAssoc($sql, [1, $datetime]);
321
    }
322
323
    public function testFetchArray()
324
    {
325
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
326
        $row = $this->connection->fetchArray($sql, [1, 'foo']);
327
328
        self::assertEquals(1, $row[0]);
329
        self::assertEquals('foo', $row[1]);
330
    }
331
332
    public function testFetchArrayWithTypes()
333
    {
334
        $datetimeString = '2010-01-01 10:10:10';
335
        $datetime       = new DateTime($datetimeString);
336
337
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
338
        $row = $this->connection->fetchArray($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
339
340
        self::assertNotFalse($row);
341
342
        $row = array_change_key_case($row, CASE_LOWER);
343
344
        self::assertEquals(1, $row[0]);
345
        self::assertStringStartsWith($datetimeString, $row[1]);
346
    }
347
348
    /**
349
     * @expectedException \Doctrine\DBAL\DBALException
350
     */
351
    public function testFetchArrayWithMissingTypes()
352
    {
353
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
354
            $this->connection->getDriver() instanceof SQLSrvDriver) {
355
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
356
        }
357
358
        $datetimeString = '2010-01-01 10:10:10';
359
        $datetime       = new DateTime($datetimeString);
360
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
361
        $row            = $this->connection->fetchArray($sql, [1, $datetime]);
362
    }
363
364
    public function testFetchColumn()
365
    {
366
        $sql     = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
367
        $testInt = $this->connection->fetchColumn($sql, [1, 'foo'], 0);
368
369
        self::assertEquals(1, $testInt);
370
371
        $sql        = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
372
        $testString = $this->connection->fetchColumn($sql, [1, 'foo'], 1);
373
374
        self::assertEquals('foo', $testString);
375
    }
376
377
    public function testFetchColumnWithTypes()
378
    {
379
        $datetimeString = '2010-01-01 10:10:10';
380
        $datetime       = new DateTime($datetimeString);
381
382
        $sql    = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
383
        $column = $this->connection->fetchColumn($sql, [1, $datetime], 1, [ParameterType::STRING, Type::DATETIME]);
384
385
        self::assertNotFalse($column);
386
387
        self::assertStringStartsWith($datetimeString, $column);
388
    }
389
390
    /**
391
     * @expectedException \Doctrine\DBAL\DBALException
392
     */
393
    public function testFetchColumnWithMissingTypes()
394
    {
395
        if ($this->connection->getDriver() instanceof MySQLiDriver ||
396
            $this->connection->getDriver() instanceof SQLSrvDriver) {
397
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
398
        }
399
400
        $datetimeString = '2010-01-01 10:10:10';
401
        $datetime       = new DateTime($datetimeString);
402
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
403
        $column         = $this->connection->fetchColumn($sql, [1, $datetime], 1);
404
    }
405
406
    /**
407
     * @group DDC-697
408
     */
409
    public function testExecuteQueryBindDateTimeType()
410
    {
411
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
412
        $stmt = $this->connection->executeQuery(
413
            $sql,
414
            [1 => new DateTime('2010-01-01 10:10:10')],
415
            [1 => Type::DATETIME]
416
        );
417
418
        self::assertEquals(1, $stmt->fetchColumn());
419
    }
420
421
    /**
422
     * @group DDC-697
423
     */
424
    public function testExecuteUpdateBindDateTimeType()
425
    {
426
        $datetime = new DateTime('2010-02-02 20:20:20');
427
428
        $sql          = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)';
429
        $affectedRows = $this->connection->executeUpdate($sql, [
430
            1 => 50,
431
            2 => 'foo',
432
            3 => $datetime,
433
        ], [
434
            1 => ParameterType::INTEGER,
435
            2 => ParameterType::STRING,
436
            3 => Type::DATETIME,
437
        ]);
438
439
        self::assertEquals(1, $affectedRows);
440
        self::assertEquals(1, $this->connection->executeQuery(
441
            'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
442
            [1 => $datetime],
443
            [1 => Type::DATETIME]
444
        )->fetchColumn());
445
    }
446
447
    /**
448
     * @group DDC-697
449
     */
450
    public function testPrepareQueryBindValueDateTimeType()
451
    {
452
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
453
        $stmt = $this->connection->prepare($sql);
454
        $stmt->bindValue(1, new DateTime('2010-01-01 10:10:10'), Type::DATETIME);
455
        $stmt->execute();
456
457
        self::assertEquals(1, $stmt->fetchColumn());
458
    }
459
460
    /**
461
     * @group DBAL-78
462
     */
463
    public function testNativeArrayListSupport()
464
    {
465
        for ($i = 100; $i < 110; $i++) {
466
            $this->connection->insert('fetch_table', ['test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10']);
467
        }
468
469
        $stmt = $this->connection->executeQuery(
470
            'SELECT test_int FROM fetch_table WHERE test_int IN (?)',
471
            [[100, 101, 102, 103, 104]],
472
            [Connection::PARAM_INT_ARRAY]
473
        );
474
475
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
476
        self::assertCount(5, $data);
477
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
478
479
        $stmt = $this->connection->executeQuery(
480
            'SELECT test_int FROM fetch_table WHERE test_string IN (?)',
481
            [['foo100', 'foo101', 'foo102', 'foo103', 'foo104']],
482
            [Connection::PARAM_STR_ARRAY]
483
        );
484
485
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
486
        self::assertCount(5, $data);
487
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
488
    }
489
490
    /**
491
     * @dataProvider getTrimExpressionData
492
     */
493
    public function testTrimExpression($value, $position, $char, $expectedResult)
494
    {
495
        $sql = 'SELECT ' .
496
            $this->connection->getDatabasePlatform()->getTrimExpression($value, $position, $char) . ' AS trimmed ' .
497
            'FROM fetch_table';
498
499
        $row = $this->connection->fetchAssoc($sql);
500
        $row = array_change_key_case($row, CASE_LOWER);
501
502
        self::assertEquals($expectedResult, $row['trimmed']);
503
    }
504
505
    public function getTrimExpressionData()
506
    {
507
        return [
508
            ['test_string', TrimMode::UNSPECIFIED, false, 'foo'],
509
            ['test_string', TrimMode::LEADING, false, 'foo'],
510
            ['test_string', TrimMode::TRAILING, false, 'foo'],
511
            ['test_string', TrimMode::BOTH, false, 'foo'],
512
            ['test_string', TrimMode::UNSPECIFIED, "'f'", 'oo'],
513
            ['test_string', TrimMode::UNSPECIFIED, "'o'", 'f'],
514
            ['test_string', TrimMode::UNSPECIFIED, "'.'", 'foo'],
515
            ['test_string', TrimMode::LEADING, "'f'", 'oo'],
516
            ['test_string', TrimMode::LEADING, "'o'", 'foo'],
517
            ['test_string', TrimMode::LEADING, "'.'", 'foo'],
518
            ['test_string', TrimMode::TRAILING, "'f'", 'foo'],
519
            ['test_string', TrimMode::TRAILING, "'o'", 'f'],
520
            ['test_string', TrimMode::TRAILING, "'.'", 'foo'],
521
            ['test_string', TrimMode::BOTH, "'f'", 'oo'],
522
            ['test_string', TrimMode::BOTH, "'o'", 'f'],
523
            ['test_string', TrimMode::BOTH, "'.'", 'foo'],
524
            ["' foo '", TrimMode::UNSPECIFIED, false, 'foo'],
525
            ["' foo '", TrimMode::LEADING, false, 'foo '],
526
            ["' foo '", TrimMode::TRAILING, false, ' foo'],
527
            ["' foo '", TrimMode::BOTH, false, 'foo'],
528
            ["' foo '", TrimMode::UNSPECIFIED, "'f'", ' foo '],
529
            ["' foo '", TrimMode::UNSPECIFIED, "'o'", ' foo '],
530
            ["' foo '", TrimMode::UNSPECIFIED, "'.'", ' foo '],
531
            ["' foo '", TrimMode::UNSPECIFIED, "' '", 'foo'],
532
            ["' foo '", TrimMode::LEADING, "'f'", ' foo '],
533
            ["' foo '", TrimMode::LEADING, "'o'", ' foo '],
534
            ["' foo '", TrimMode::LEADING, "'.'", ' foo '],
535
            ["' foo '", TrimMode::LEADING, "' '", 'foo '],
536
            ["' foo '", TrimMode::TRAILING, "'f'", ' foo '],
537
            ["' foo '", TrimMode::TRAILING, "'o'", ' foo '],
538
            ["' foo '", TrimMode::TRAILING, "'.'", ' foo '],
539
            ["' foo '", TrimMode::TRAILING, "' '", ' foo'],
540
            ["' foo '", TrimMode::BOTH, "'f'", ' foo '],
541
            ["' foo '", TrimMode::BOTH, "'o'", ' foo '],
542
            ["' foo '", TrimMode::BOTH, "'.'", ' foo '],
543
            ["' foo '", TrimMode::BOTH, "' '", 'foo'],
544
        ];
545
    }
546
547
    /**
548
     * @group DDC-1014
549
     */
550
    public function testDateArithmetics()
551
    {
552
        $p    = $this->connection->getDatabasePlatform();
553
        $sql  = 'SELECT ';
554
        $sql .= $p->getDateAddSecondsExpression('test_datetime', 1) . ' AS add_seconds, ';
555
        $sql .= $p->getDateSubSecondsExpression('test_datetime', 1) . ' AS sub_seconds, ';
556
        $sql .= $p->getDateAddMinutesExpression('test_datetime', 5) . ' AS add_minutes, ';
557
        $sql .= $p->getDateSubMinutesExpression('test_datetime', 5) . ' AS sub_minutes, ';
558
        $sql .= $p->getDateAddHourExpression('test_datetime', 3) . ' AS add_hour, ';
559
        $sql .= $p->getDateSubHourExpression('test_datetime', 3) . ' AS sub_hour, ';
560
        $sql .= $p->getDateAddDaysExpression('test_datetime', 10) . ' AS add_days, ';
561
        $sql .= $p->getDateSubDaysExpression('test_datetime', 10) . ' AS sub_days, ';
562
        $sql .= $p->getDateAddWeeksExpression('test_datetime', 1) . ' AS add_weeks, ';
563
        $sql .= $p->getDateSubWeeksExpression('test_datetime', 1) . ' AS sub_weeks, ';
564
        $sql .= $p->getDateAddMonthExpression('test_datetime', 2) . ' AS add_month, ';
565
        $sql .= $p->getDateSubMonthExpression('test_datetime', 2) . ' AS sub_month, ';
566
        $sql .= $p->getDateAddQuartersExpression('test_datetime', 3) . ' AS add_quarters, ';
567
        $sql .= $p->getDateSubQuartersExpression('test_datetime', 3) . ' AS sub_quarters, ';
568
        $sql .= $p->getDateAddYearsExpression('test_datetime', 6) . ' AS add_years, ';
569
        $sql .= $p->getDateSubYearsExpression('test_datetime', 6) . ' AS sub_years ';
570
        $sql .= 'FROM fetch_table';
571
572
        $row = $this->connection->fetchAssoc($sql);
573
        $row = array_change_key_case($row, CASE_LOWER);
574
575
        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');
576
        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');
577
        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');
578
        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');
579
        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');
580
        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');
581
        self::assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), 'Adding date should end up on 2010-01-11');
582
        self::assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), 'Subtracting date should end up on 2009-12-22');
583
        self::assertEquals('2010-01-08', date('Y-m-d', strtotime($row['add_weeks'])), 'Adding week should end up on 2010-01-08');
584
        self::assertEquals('2009-12-25', date('Y-m-d', strtotime($row['sub_weeks'])), 'Subtracting week should end up on 2009-12-25');
585
        self::assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), 'Adding month should end up on 2010-03-01');
586
        self::assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), 'Subtracting month should end up on 2009-11-01');
587
        self::assertEquals('2010-10-01', date('Y-m-d', strtotime($row['add_quarters'])), 'Adding quarters should end up on 2010-04-01');
588
        self::assertEquals('2009-04-01', date('Y-m-d', strtotime($row['sub_quarters'])), 'Subtracting quarters should end up on 2009-10-01');
589
        self::assertEquals('2016-01-01', date('Y-m-d', strtotime($row['add_years'])), 'Adding years should end up on 2016-01-01');
590
        self::assertEquals('2004-01-01', date('Y-m-d', strtotime($row['sub_years'])), 'Subtracting years should end up on 2004-01-01');
591
    }
592
593
    public function testSqliteDateArithmeticWithDynamicInterval()
594
    {
595
        $platform = $this->connection->getDatabasePlatform();
596
597
        if (! $platform instanceof SqlitePlatform) {
598
            $this->markTestSkipped('test is for sqlite only');
599
        }
600
601
        $table = new Table('fetch_table_date_math');
602
        $table->addColumn('test_date', 'date');
603
        $table->addColumn('test_days', 'integer');
604
        $table->setPrimaryKey(['test_date']);
605
606
        $sm = $this->connection->getSchemaManager();
607
        $sm->createTable($table);
608
609
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-01-01', 'test_days' => 10]);
610
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-06-01', 'test_days' => 20]);
611
612
        $sql  = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
613
        $sql .= $platform->getDateSubDaysExpression('test_date', 'test_days') . " < '2010-05-12'";
0 ignored issues
show
'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

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