Failed Conditions
Push — master ( 30b923...92920e )
by Marco
19s queued 13s
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\DBALException;
8
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
9
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver;
10
use Doctrine\DBAL\Driver\PDOConnection;
11
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
12
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
13
use Doctrine\DBAL\FetchMode;
14
use Doctrine\DBAL\ParameterType;
15
use Doctrine\DBAL\Platforms\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() : void
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
     */
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
254
        $this->expectException(DBALException::class);
255
256
        $this->connection->fetchAll($sql, [1, $datetime]);
257
    }
258
259
    public function testFetchBoth()
260
    {
261
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
262
        $row = $this->connection->executeQuery($sql, [1, 'foo'])->fetch(FetchMode::MIXED);
263
264
        self::assertNotFalse($row);
265
266
        $row = array_change_key_case($row, CASE_LOWER);
267
268
        self::assertEquals(1, $row['test_int']);
269
        self::assertEquals('foo', $row['test_string']);
270
        self::assertEquals(1, $row[0]);
271
        self::assertEquals('foo', $row[1]);
272
    }
273
274
    public function testFetchNoResult()
275
    {
276
        self::assertFalse(
277
            $this->connection->executeQuery('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])->fetch()
278
        );
279
    }
280
281
    public function testFetchAssoc()
282
    {
283
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
284
        $row = $this->connection->fetchAssoc($sql, [1, 'foo']);
285
286
        self::assertNotFalse($row);
287
288
        $row = array_change_key_case($row, CASE_LOWER);
289
290
        self::assertEquals(1, $row['test_int']);
291
        self::assertEquals('foo', $row['test_string']);
292
    }
293
294
    public function testFetchAssocWithTypes()
295
    {
296
        $datetimeString = '2010-01-01 10:10:10';
297
        $datetime       = new DateTime($datetimeString);
298
299
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
300
        $row = $this->connection->fetchAssoc($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
301
302
        self::assertNotFalse($row);
303
304
        $row = array_change_key_case($row, CASE_LOWER);
305
306
        self::assertEquals(1, $row['test_int']);
307
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
308
    }
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
321
        $this->expectException(DBALException::class);
322
323
        $this->connection->fetchAssoc($sql, [1, $datetime]);
324
    }
325
326
    public function testFetchArray()
327
    {
328
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
329
        $row = $this->connection->fetchArray($sql, [1, 'foo']);
330
331
        self::assertEquals(1, $row[0]);
332
        self::assertEquals('foo', $row[1]);
333
    }
334
335
    public function testFetchArrayWithTypes()
336
    {
337
        $datetimeString = '2010-01-01 10:10:10';
338
        $datetime       = new DateTime($datetimeString);
339
340
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
341
        $row = $this->connection->fetchArray($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
342
343
        self::assertNotFalse($row);
344
345
        $row = array_change_key_case($row, CASE_LOWER);
346
347
        self::assertEquals(1, $row[0]);
348
        self::assertStringStartsWith($datetimeString, $row[1]);
349
    }
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
362
        $this->expectException(DBALException::class);
363
364
        $this->connection->fetchArray($sql, [1, $datetime]);
365
    }
366
367
    public function testFetchColumn()
368
    {
369
        $sql     = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
370
        $testInt = $this->connection->fetchColumn($sql, [1, 'foo'], 0);
371
372
        self::assertEquals(1, $testInt);
373
374
        $sql        = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
375
        $testString = $this->connection->fetchColumn($sql, [1, 'foo'], 1);
376
377
        self::assertEquals('foo', $testString);
378
    }
379
380
    public function testFetchColumnWithTypes()
381
    {
382
        $datetimeString = '2010-01-01 10:10:10';
383
        $datetime       = new DateTime($datetimeString);
384
385
        $sql    = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
386
        $column = $this->connection->fetchColumn($sql, [1, $datetime], 1, [ParameterType::STRING, Type::DATETIME]);
387
388
        self::assertNotFalse($column);
389
390
        self::assertStringStartsWith($datetimeString, $column);
391
    }
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
404
        $this->expectException(DBALException::class);
405
406
        $this->connection->fetchColumn($sql, [1, $datetime], 1);
407
    }
408
409
    /**
410
     * @group DDC-697
411
     */
412
    public function testExecuteQueryBindDateTimeType()
413
    {
414
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
415
        $stmt = $this->connection->executeQuery(
416
            $sql,
417
            [1 => new DateTime('2010-01-01 10:10:10')],
418
            [1 => Type::DATETIME]
419
        );
420
421
        self::assertEquals(1, $stmt->fetchColumn());
422
    }
423
424
    /**
425
     * @group DDC-697
426
     */
427
    public function testExecuteUpdateBindDateTimeType()
428
    {
429
        $datetime = new DateTime('2010-02-02 20:20:20');
430
431
        $sql          = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)';
432
        $affectedRows = $this->connection->executeUpdate($sql, [
433
            1 => 50,
434
            2 => 'foo',
435
            3 => $datetime,
436
        ], [
437
            1 => ParameterType::INTEGER,
438
            2 => ParameterType::STRING,
439
            3 => Type::DATETIME,
440
        ]);
441
442
        self::assertEquals(1, $affectedRows);
443
        self::assertEquals(1, $this->connection->executeQuery(
444
            'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
445
            [1 => $datetime],
446
            [1 => Type::DATETIME]
447
        )->fetchColumn());
448
    }
449
450
    /**
451
     * @group DDC-697
452
     */
453
    public function testPrepareQueryBindValueDateTimeType()
454
    {
455
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
456
        $stmt = $this->connection->prepare($sql);
457
        $stmt->bindValue(1, new DateTime('2010-01-01 10:10:10'), Type::DATETIME);
458
        $stmt->execute();
459
460
        self::assertEquals(1, $stmt->fetchColumn());
461
    }
462
463
    /**
464
     * @group DBAL-78
465
     */
466
    public function testNativeArrayListSupport()
467
    {
468
        for ($i = 100; $i < 110; $i++) {
469
            $this->connection->insert('fetch_table', ['test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10']);
470
        }
471
472
        $stmt = $this->connection->executeQuery(
473
            'SELECT test_int FROM fetch_table WHERE test_int IN (?)',
474
            [[100, 101, 102, 103, 104]],
475
            [Connection::PARAM_INT_ARRAY]
476
        );
477
478
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
479
        self::assertCount(5, $data);
480
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
481
482
        $stmt = $this->connection->executeQuery(
483
            'SELECT test_int FROM fetch_table WHERE test_string IN (?)',
484
            [['foo100', 'foo101', 'foo102', 'foo103', 'foo104']],
485
            [Connection::PARAM_STR_ARRAY]
486
        );
487
488
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
489
        self::assertCount(5, $data);
490
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
491
    }
492
493
    /**
494
     * @dataProvider getTrimExpressionData
495
     */
496
    public function testTrimExpression($value, $position, $char, $expectedResult)
497
    {
498
        $sql = 'SELECT ' .
499
            $this->connection->getDatabasePlatform()->getTrimExpression($value, $position, $char) . ' AS trimmed ' .
500
            'FROM fetch_table';
501
502
        $row = $this->connection->fetchAssoc($sql);
503
        $row = array_change_key_case($row, CASE_LOWER);
504
505
        self::assertEquals($expectedResult, $row['trimmed']);
506
    }
507
508
    public function getTrimExpressionData()
509
    {
510
        return [
511
            ['test_string', TrimMode::UNSPECIFIED, false, 'foo'],
512
            ['test_string', TrimMode::LEADING, false, 'foo'],
513
            ['test_string', TrimMode::TRAILING, false, 'foo'],
514
            ['test_string', TrimMode::BOTH, false, 'foo'],
515
            ['test_string', TrimMode::UNSPECIFIED, "'f'", 'oo'],
516
            ['test_string', TrimMode::UNSPECIFIED, "'o'", 'f'],
517
            ['test_string', TrimMode::UNSPECIFIED, "'.'", 'foo'],
518
            ['test_string', TrimMode::LEADING, "'f'", 'oo'],
519
            ['test_string', TrimMode::LEADING, "'o'", 'foo'],
520
            ['test_string', TrimMode::LEADING, "'.'", 'foo'],
521
            ['test_string', TrimMode::TRAILING, "'f'", 'foo'],
522
            ['test_string', TrimMode::TRAILING, "'o'", 'f'],
523
            ['test_string', TrimMode::TRAILING, "'.'", 'foo'],
524
            ['test_string', TrimMode::BOTH, "'f'", 'oo'],
525
            ['test_string', TrimMode::BOTH, "'o'", 'f'],
526
            ['test_string', TrimMode::BOTH, "'.'", 'foo'],
527
            ["' foo '", TrimMode::UNSPECIFIED, false, 'foo'],
528
            ["' foo '", TrimMode::LEADING, false, 'foo '],
529
            ["' foo '", TrimMode::TRAILING, false, ' foo'],
530
            ["' foo '", TrimMode::BOTH, false, 'foo'],
531
            ["' foo '", TrimMode::UNSPECIFIED, "'f'", ' foo '],
532
            ["' foo '", TrimMode::UNSPECIFIED, "'o'", ' foo '],
533
            ["' foo '", TrimMode::UNSPECIFIED, "'.'", ' foo '],
534
            ["' foo '", TrimMode::UNSPECIFIED, "' '", 'foo'],
535
            ["' foo '", TrimMode::LEADING, "'f'", ' foo '],
536
            ["' foo '", TrimMode::LEADING, "'o'", ' foo '],
537
            ["' foo '", TrimMode::LEADING, "'.'", ' foo '],
538
            ["' foo '", TrimMode::LEADING, "' '", 'foo '],
539
            ["' foo '", TrimMode::TRAILING, "'f'", ' foo '],
540
            ["' foo '", TrimMode::TRAILING, "'o'", ' foo '],
541
            ["' foo '", TrimMode::TRAILING, "'.'", ' foo '],
542
            ["' foo '", TrimMode::TRAILING, "' '", ' foo'],
543
            ["' foo '", TrimMode::BOTH, "'f'", ' foo '],
544
            ["' foo '", TrimMode::BOTH, "'o'", ' foo '],
545
            ["' foo '", TrimMode::BOTH, "'.'", ' foo '],
546
            ["' foo '", TrimMode::BOTH, "' '", 'foo'],
547
        ];
548
    }
549
550
    /**
551
     * @group DDC-1014
552
     */
553
    public function testDateArithmetics()
554
    {
555
        $p    = $this->connection->getDatabasePlatform();
556
        $sql  = 'SELECT ';
557
        $sql .= $p->getDateAddSecondsExpression('test_datetime', 1) . ' AS add_seconds, ';
558
        $sql .= $p->getDateSubSecondsExpression('test_datetime', 1) . ' AS sub_seconds, ';
559
        $sql .= $p->getDateAddMinutesExpression('test_datetime', 5) . ' AS add_minutes, ';
560
        $sql .= $p->getDateSubMinutesExpression('test_datetime', 5) . ' AS sub_minutes, ';
561
        $sql .= $p->getDateAddHourExpression('test_datetime', 3) . ' AS add_hour, ';
562
        $sql .= $p->getDateSubHourExpression('test_datetime', 3) . ' AS sub_hour, ';
563
        $sql .= $p->getDateAddDaysExpression('test_datetime', 10) . ' AS add_days, ';
564
        $sql .= $p->getDateSubDaysExpression('test_datetime', 10) . ' AS sub_days, ';
565
        $sql .= $p->getDateAddWeeksExpression('test_datetime', 1) . ' AS add_weeks, ';
566
        $sql .= $p->getDateSubWeeksExpression('test_datetime', 1) . ' AS sub_weeks, ';
567
        $sql .= $p->getDateAddMonthExpression('test_datetime', 2) . ' AS add_month, ';
568
        $sql .= $p->getDateSubMonthExpression('test_datetime', 2) . ' AS sub_month, ';
569
        $sql .= $p->getDateAddQuartersExpression('test_datetime', 3) . ' AS add_quarters, ';
570
        $sql .= $p->getDateSubQuartersExpression('test_datetime', 3) . ' AS sub_quarters, ';
571
        $sql .= $p->getDateAddYearsExpression('test_datetime', 6) . ' AS add_years, ';
572
        $sql .= $p->getDateSubYearsExpression('test_datetime', 6) . ' AS sub_years ';
573
        $sql .= 'FROM fetch_table';
574
575
        $row = $this->connection->fetchAssoc($sql);
576
        $row = array_change_key_case($row, CASE_LOWER);
577
578
        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');
579
        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');
580
        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');
581
        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');
582
        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');
583
        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');
584
        self::assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), 'Adding date should end up on 2010-01-11');
585
        self::assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), 'Subtracting date should end up on 2009-12-22');
586
        self::assertEquals('2010-01-08', date('Y-m-d', strtotime($row['add_weeks'])), 'Adding week should end up on 2010-01-08');
587
        self::assertEquals('2009-12-25', date('Y-m-d', strtotime($row['sub_weeks'])), 'Subtracting week should end up on 2009-12-25');
588
        self::assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), 'Adding month should end up on 2010-03-01');
589
        self::assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), 'Subtracting month should end up on 2009-11-01');
590
        self::assertEquals('2010-10-01', date('Y-m-d', strtotime($row['add_quarters'])), 'Adding quarters should end up on 2010-04-01');
591
        self::assertEquals('2009-04-01', date('Y-m-d', strtotime($row['sub_quarters'])), 'Subtracting quarters should end up on 2009-10-01');
592
        self::assertEquals('2016-01-01', date('Y-m-d', strtotime($row['add_years'])), 'Adding years should end up on 2016-01-01');
593
        self::assertEquals('2004-01-01', date('Y-m-d', strtotime($row['sub_years'])), 'Subtracting years should end up on 2004-01-01');
594
    }
595
596
    public function testSqliteDateArithmeticWithDynamicInterval()
597
    {
598
        $platform = $this->connection->getDatabasePlatform();
599
600
        if (! $platform instanceof SqlitePlatform) {
601
            $this->markTestSkipped('test is for sqlite only');
602
        }
603
604
        $table = new Table('fetch_table_date_math');
605
        $table->addColumn('test_date', 'date');
606
        $table->addColumn('test_days', 'integer');
607
        $table->setPrimaryKey(['test_date']);
608
609
        $sm = $this->connection->getSchemaManager();
610
        $sm->createTable($table);
611
612
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-01-01', 'test_days' => 10]);
613
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-06-01', 'test_days' => 20]);
614
615
        $sql  = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
616
        $sql .= $platform->getDateSubDaysExpression('test_date', 'test_days') . " < '2010-05-12'";
617
618
        $rowCount = $this->connection->fetchColumn($sql, [], 0);
619
620
        $this->assertEquals(1, $rowCount);
621
    }
622
623
    public function testLocateExpression()
624
    {
625
        $platform = $this->connection->getDatabasePlatform();
626
627
        $sql  = 'SELECT ';
628
        $sql .= $platform->getLocateExpression('test_string', "'oo'") . ' AS locate1, ';
629
        $sql .= $platform->getLocateExpression('test_string', "'foo'") . ' AS locate2, ';
630
        $sql .= $platform->getLocateExpression('test_string', "'bar'") . ' AS locate3, ';
631
        $sql .= $platform->getLocateExpression('test_string', 'test_string') . ' AS locate4, ';
632
        $sql .= $platform->getLocateExpression("'foo'", 'test_string') . ' AS locate5, ';
633
        $sql .= $platform->getLocateExpression("'barfoobaz'", 'test_string') . ' AS locate6, ';
634
        $sql .= $platform->getLocateExpression("'bar'", 'test_string') . ' AS locate7, ';
635
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 2) . ' AS locate8, ';
636
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 3) . ' AS locate9 ';
637
        $sql .= 'FROM fetch_table';
638
639
        $row = $this->connection->fetchAssoc($sql);
640
        $row = array_change_key_case($row, CASE_LOWER);
641
642
        self::assertEquals(2, $row['locate1']);
643
        self::assertEquals(1, $row['locate2']);
644
        self::assertEquals(0, $row['locate3']);
645
        self::assertEquals(1, $row['locate4']);
646
        self::assertEquals(1, $row['locate5']);
647
        self::assertEquals(4, $row['locate6']);
648
        self::assertEquals(0, $row['locate7']);
649
        self::assertEquals(2, $row['locate8']);
650
        self::assertEquals(0, $row['locate9']);
651
    }
652
653
    public function testQuoteSQLInjection()
654
    {
655
        $sql  = 'SELECT * FROM fetch_table WHERE test_string = ' . $this->connection->quote("bar' OR '1'='1");
656
        $rows = $this->connection->fetchAll($sql);
657
658
        self::assertCount(0, $rows, 'no result should be returned, otherwise SQL injection is possible');
659
    }
660
661
    /**
662
     * @group DDC-1213
663
     */
664
    public function testBitComparisonExpressionSupport()
665
    {
666
        $this->connection->exec('DELETE FROM fetch_table');
667
        $platform = $this->connection->getDatabasePlatform();
668
        $bitmap   = [];
669
670
        for ($i = 2; $i < 9; $i += 2) {
671
            $bitmap[$i] = [
672
                'bit_or'    => ($i | 2),
673
                'bit_and'   => ($i & 2),
674
            ];
675
            $this->connection->insert('fetch_table', [
676
                'test_int'      => $i,
677
                'test_string'   => json_encode($bitmap[$i]),
678
                'test_datetime' => '2010-01-01 10:10:10',
679
            ]);
680
        }
681
682
        $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...
683
        $sql[] = 'test_int, ';
684
        $sql[] = 'test_string, ';
685
        $sql[] = $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or, ';
686
        $sql[] = $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and ';
687
        $sql[] = 'FROM fetch_table';
688
689
        $stmt = $this->connection->executeQuery(implode(PHP_EOL, $sql));
690
        $data = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
691
692
        self::assertCount(4, $data);
693
        self::assertEquals(count($bitmap), count($data));
694
        foreach ($data as $row) {
695
            $row = array_change_key_case($row, CASE_LOWER);
696
697
            self::assertArrayHasKey('test_int', $row);
698
699
            $id = $row['test_int'];
700
701
            self::assertArrayHasKey($id, $bitmap);
702
            self::assertArrayHasKey($id, $bitmap);
703
704
            self::assertArrayHasKey('bit_or', $row);
705
            self::assertArrayHasKey('bit_and', $row);
706
707
            self::assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
708
            self::assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
709
        }
710
    }
711
712
    public function testSetDefaultFetchMode()
713
    {
714
        $stmt = $this->connection->query('SELECT * FROM fetch_table');
715
        $stmt->setFetchMode(FetchMode::NUMERIC);
716
717
        $row = array_keys($stmt->fetch());
718
        self::assertCount(0, array_filter($row, static function ($v) {
719
            return ! is_numeric($v);
720
        }), 'should be no non-numerical elements in the result.');
721
    }
722
723
    /**
724
     * @group DBAL-1091
725
     */
726
    public function testFetchAllStyleObject()
727
    {
728
        $this->setupFixture();
729
730
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
731
        $stmt = $this->connection->prepare($sql);
732
733
        $stmt->execute();
734
735
        $results = $stmt->fetchAll(FetchMode::STANDARD_OBJECT);
736
737
        self::assertCount(1, $results);
738
        self::assertInstanceOf('stdClass', $results[0]);
739
740
        self::assertEquals(
741
            1,
742
            property_exists($results[0], 'test_int') ? $results[0]->test_int : $results[0]->TEST_INT
743
        );
744
        self::assertEquals(
745
            'foo',
746
            property_exists($results[0], 'test_string') ? $results[0]->test_string : $results[0]->TEST_STRING
747
        );
748
        self::assertStringStartsWith(
749
            '2010-01-01 10:10:10',
750
            property_exists($results[0], 'test_datetime') ? $results[0]->test_datetime : $results[0]->TEST_DATETIME
751
        );
752
    }
753
754
    /**
755
     * @group DBAL-196
756
     */
757
    public function testFetchAllSupportFetchClass()
758
    {
759
        $this->beforeFetchClassTest();
760
        $this->setupFixture();
761
762
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
763
        $stmt = $this->connection->prepare($sql);
764
        $stmt->execute();
765
766
        $results = $stmt->fetchAll(
767
            FetchMode::CUSTOM_OBJECT,
768
            MyFetchClass::class
769
        );
770
771
        self::assertCount(1, $results);
772
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
773
774
        self::assertEquals(1, $results[0]->test_int);
775
        self::assertEquals('foo', $results[0]->test_string);
776
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
777
    }
778
779
    /**
780
     * @group DBAL-241
781
     */
782
    public function testFetchAllStyleColumn()
783
    {
784
        $sql = 'DELETE FROM fetch_table';
785
        $this->connection->executeUpdate($sql);
786
787
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo']);
788
        $this->connection->insert('fetch_table', ['test_int' => 10, 'test_string' => 'foo']);
789
790
        $sql  = 'SELECT test_int FROM fetch_table';
791
        $rows = $this->connection->query($sql)->fetchAll(FetchMode::COLUMN);
792
793
        self::assertEquals([1, 10], $rows);
794
    }
795
796
    /**
797
     * @group DBAL-214
798
     */
799
    public function testSetFetchModeClassFetchAll()
800
    {
801
        $this->beforeFetchClassTest();
802
        $this->setupFixture();
803
804
        $sql  = 'SELECT * FROM fetch_table';
805
        $stmt = $this->connection->query($sql);
806
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
807
808
        $results = $stmt->fetchAll();
809
810
        self::assertCount(1, $results);
811
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
812
813
        self::assertEquals(1, $results[0]->test_int);
814
        self::assertEquals('foo', $results[0]->test_string);
815
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
816
    }
817
818
    /**
819
     * @group DBAL-214
820
     */
821
    public function testSetFetchModeClassFetch()
822
    {
823
        $this->beforeFetchClassTest();
824
        $this->setupFixture();
825
826
        $sql  = 'SELECT * FROM fetch_table';
827
        $stmt = $this->connection->query($sql);
828
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
829
830
        $results = [];
831
        while ($row = $stmt->fetch()) {
832
            $results[] = $row;
833
        }
834
835
        self::assertCount(1, $results);
836
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
837
838
        self::assertEquals(1, $results[0]->test_int);
839
        self::assertEquals('foo', $results[0]->test_string);
840
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
841
    }
842
843
    /**
844
     * @group DBAL-257
845
     */
846
    public function testEmptyFetchColumnReturnsFalse()
847
    {
848
        $this->connection->beginTransaction();
849
        $this->connection->exec('DELETE FROM fetch_table');
850
        self::assertFalse($this->connection->fetchColumn('SELECT test_int FROM fetch_table'));
851
        self::assertFalse($this->connection->query('SELECT test_int FROM fetch_table')->fetchColumn());
852
        $this->connection->rollBack();
853
    }
854
855
    /**
856
     * @group DBAL-339
857
     */
858
    public function testSetFetchModeOnDbalStatement()
859
    {
860
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
861
        $stmt = $this->connection->executeQuery($sql, [1, 'foo']);
862
        $stmt->setFetchMode(FetchMode::NUMERIC);
863
864
        $row = $stmt->fetch();
865
866
        self::assertArrayHasKey(0, $row);
867
        self::assertArrayHasKey(1, $row);
868
        self::assertFalse($stmt->fetch());
869
    }
870
871
    /**
872
     * @group DBAL-435
873
     */
874
    public function testEmptyParameters()
875
    {
876
        $sql  = 'SELECT * FROM fetch_table WHERE test_int IN (?)';
877
        $stmt = $this->connection->executeQuery($sql, [[]], [Connection::PARAM_INT_ARRAY]);
878
        $rows = $stmt->fetchAll();
879
880
        self::assertEquals([], $rows);
881
    }
882
883
    /**
884
     * @group DBAL-1028
885
     */
886
    public function testFetchColumnNullValue()
887
    {
888
        $this->connection->executeUpdate(
889
            'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)',
890
            [2, 'foo']
891
        );
892
893
        self::assertNull(
894
            $this->connection->fetchColumn('SELECT test_datetime FROM fetch_table WHERE test_int = ?', [2])
895
        );
896
    }
897
898
    /**
899
     * @group DBAL-1028
900
     */
901
    public function testFetchColumnNoResult()
902
    {
903
        self::assertFalse(
904
            $this->connection->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])
905
        );
906
    }
907
908
    private function setupFixture()
909
    {
910
        $this->connection->exec('DELETE FROM fetch_table');
911
        $this->connection->insert('fetch_table', [
912
            'test_int'      => 1,
913
            'test_string'   => 'foo',
914
            'test_datetime' => '2010-01-01 10:10:10',
915
        ]);
916
    }
917
918
    private function beforeFetchClassTest()
919
    {
920
        $driver = $this->connection->getDriver();
921
922
        if ($driver instanceof Oci8Driver) {
923
            $this->markTestSkipped('Not supported by OCI8');
924
        }
925
926
        if ($driver instanceof MySQLiDriver) {
927
            $this->markTestSkipped('Mysqli driver dont support this feature.');
928
        }
929
930
        if (! $driver instanceof PDOOracleDriver) {
931
            return;
932
        }
933
934
        /** @var PDOConnection $connection */
935
        $connection = $this->connection->getWrappedConnection();
936
        $connection->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
937
    }
938
}
939
940
class MyFetchClass
941
{
942
    /** @var int */
943
    public $test_int;
944
945
    /** @var string */
946
    public $test_string;
947
948
    /** @var string */
949
    public $test_datetime;
950
}
951