Completed
Push — develop ( de019a...a640b8 )
by Marco
19s queued 13s
created

DataAccessTest::testTrimExpressionInvalidMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

305
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
306
307
        self::assertEquals(1, $row['test_int']);
308
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
309
    }
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
322
        $this->expectException(DBALException::class);
323
324
        $this->connection->fetchAssoc($sql, [1, $datetime]);
325
    }
326
327
    public function testFetchArray()
328
    {
329
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
330
        $row = $this->connection->fetchArray($sql, [1, 'foo']);
331
332
        self::assertEquals(1, $row[0]);
333
        self::assertEquals('foo', $row[1]);
334
    }
335
336
    public function testFetchArrayWithTypes()
337
    {
338
        $datetimeString = '2010-01-01 10:10:10';
339
        $datetime       = new DateTime($datetimeString);
340
341
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
342
        $row = $this->connection->fetchArray($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
343
344
        self::assertNotFalse($row);
345
346
        $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

346
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
347
348
        self::assertEquals(1, $row[0]);
349
        self::assertStringStartsWith($datetimeString, $row[1]);
350
    }
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
363
        $this->expectException(DBALException::class);
364
365
        $this->connection->fetchArray($sql, [1, $datetime]);
366
    }
367
368
    public function testFetchColumn()
369
    {
370
        $sql     = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
371
        $testInt = $this->connection->fetchColumn($sql, [1, 'foo'], 0);
372
373
        self::assertEquals(1, $testInt);
374
375
        $sql        = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
376
        $testString = $this->connection->fetchColumn($sql, [1, 'foo'], 1);
377
378
        self::assertEquals('foo', $testString);
379
    }
380
381
    public function testFetchColumnWithTypes()
382
    {
383
        $datetimeString = '2010-01-01 10:10:10';
384
        $datetime       = new DateTime($datetimeString);
385
386
        $sql    = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
387
        $column = $this->connection->fetchColumn($sql, [1, $datetime], 1, [ParameterType::STRING, Type::DATETIME]);
388
389
        self::assertNotFalse($column);
390
391
        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

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

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

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