Passed
Pull Request — master (#3108)
by Sergei
61:16
created

DataAccessTest::testLocateExpression()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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

272
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, \CASE_LOWER);
Loading history...
273
274
        self::assertEquals(1, $row['test_int']);
275
        self::assertEquals('foo', $row['test_string']);
276
    }
277
278
    public function testFetchAssocWithTypes()
279
    {
280
        $datetimeString = '2010-01-01 10:10:10';
281
        $datetime = new \DateTime($datetimeString);
282
283
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
284
        $row = $this->_conn->fetchAssoc($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
285
286
        self::assertNotFalse($row);
287
288
        $row = array_change_key_case($row, \CASE_LOWER);
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type boolean; 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

288
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, \CASE_LOWER);
Loading history...
289
290
        self::assertEquals(1, $row['test_int']);
291
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
292
    }
293
294
    /**
295
     * @expectedException \Doctrine\DBAL\DBALException
296
     */
297
    public function testFetchAssocWithMissingTypes()
298
    {
299
        if ($this->_conn->getDriver() instanceof \Doctrine\DBAL\Driver\Mysqli\Driver ||
300
            $this->_conn->getDriver() instanceof \Doctrine\DBAL\Driver\SQLSrv\Driver) {
301
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
302
        }
303
304
        $datetimeString = '2010-01-01 10:10:10';
305
        $datetime = new \DateTime($datetimeString);
306
        $sql = "SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?";
307
        $row = $this->_conn->fetchAssoc($sql, array(1, $datetime));
0 ignored issues
show
Unused Code introduced by
The assignment to $row is dead and can be removed.
Loading history...
308
    }
309
310
    public function testFetchArray()
311
    {
312
        $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
313
        $row = $this->_conn->fetchArray($sql, array(1, 'foo'));
314
315
        self::assertEquals(1, $row[0]);
316
        self::assertEquals('foo', $row[1]);
317
    }
318
319
    public function testFetchArrayWithTypes()
320
    {
321
        $datetimeString = '2010-01-01 10:10:10';
322
        $datetime = new \DateTime($datetimeString);
323
324
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
325
        $row = $this->_conn->fetchArray($sql, [1, $datetime], [ParameterType::STRING, Type::DATETIME]);
326
327
        self::assertNotFalse($row);
328
329
        $row = array_change_key_case($row, \CASE_LOWER);
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type boolean; 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

329
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, \CASE_LOWER);
Loading history...
330
331
        self::assertEquals(1, $row[0]);
332
        self::assertStringStartsWith($datetimeString, $row[1]);
333
    }
334
335
    /**
336
     * @expectedException \Doctrine\DBAL\DBALException
337
     */
338
    public function testFetchArrayWithMissingTypes()
339
    {
340
        if ($this->_conn->getDriver() instanceof \Doctrine\DBAL\Driver\Mysqli\Driver ||
341
            $this->_conn->getDriver() instanceof \Doctrine\DBAL\Driver\SQLSrv\Driver) {
342
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
343
        }
344
345
        $datetimeString = '2010-01-01 10:10:10';
346
        $datetime = new \DateTime($datetimeString);
347
        $sql = "SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?";
348
        $row = $this->_conn->fetchArray($sql, array(1, $datetime));
0 ignored issues
show
Unused Code introduced by
The assignment to $row is dead and can be removed.
Loading history...
349
    }
350
351
    public function testFetchColumn()
352
    {
353
        $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
354
        $testInt = $this->_conn->fetchColumn($sql, array(1, 'foo'), 0);
355
356
        self::assertEquals(1, $testInt);
357
358
        $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
359
        $testString = $this->_conn->fetchColumn($sql, array(1, 'foo'), 1);
360
361
        self::assertEquals('foo', $testString);
362
    }
363
364
    public function testFetchColumnWithTypes()
365
    {
366
        $datetimeString = '2010-01-01 10:10:10';
367
        $datetime = new \DateTime($datetimeString);
368
369
        $sql    = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
370
        $column = $this->_conn->fetchColumn($sql, [1, $datetime], 1, [ParameterType::STRING, Type::DATETIME]);
371
372
        self::assertNotFalse($column);
373
374
        self::assertStringStartsWith($datetimeString, $column);
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type boolean; 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

374
        self::assertStringStartsWith($datetimeString, /** @scrutinizer ignore-type */ $column);
Loading history...
375
    }
376
377
    /**
378
     * @expectedException \Doctrine\DBAL\DBALException
379
     */
380
    public function testFetchColumnWithMissingTypes()
381
    {
382
        if ($this->_conn->getDriver() instanceof \Doctrine\DBAL\Driver\Mysqli\Driver ||
383
            $this->_conn->getDriver() instanceof \Doctrine\DBAL\Driver\SQLSrv\Driver) {
384
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
385
        }
386
387
        $datetimeString = '2010-01-01 10:10:10';
388
        $datetime = new \DateTime($datetimeString);
389
        $sql = "SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?";
390
        $column = $this->_conn->fetchColumn($sql, array(1, $datetime), 1);
0 ignored issues
show
Unused Code introduced by
The assignment to $column is dead and can be removed.
Loading history...
391
    }
392
393
    /**
394
     * @group DDC-697
395
     */
396
    public function testExecuteQueryBindDateTimeType()
397
    {
398
        $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
399
        $stmt = $this->_conn->executeQuery($sql,
400
            array(1 => new \DateTime('2010-01-01 10:10:10')),
401
            array(1 => Type::DATETIME)
402
        );
403
404
        self::assertEquals(1, $stmt->fetchColumn());
405
    }
406
407
    /**
408
     * @group DDC-697
409
     */
410
    public function testExecuteUpdateBindDateTimeType()
411
    {
412
        $datetime = new \DateTime('2010-02-02 20:20:20');
413
414
        $sql = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)';
415
        $affectedRows = $this->_conn->executeUpdate($sql, [
416
            1 => 50,
417
            2 => 'foo',
418
            3 => $datetime,
419
        ], [
420
            1 => ParameterType::INTEGER,
421
            2 => ParameterType::STRING,
422
            3 => Type::DATETIME,
423
        ]);
424
425
        self::assertEquals(1, $affectedRows);
426
        self::assertEquals(1, $this->_conn->executeQuery(
427
            'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
428
            array(1 => $datetime),
429
            array(1 => Type::DATETIME)
430
        )->fetchColumn());
431
    }
432
433
    /**
434
     * @group DDC-697
435
     */
436
    public function testPrepareQueryBindValueDateTimeType()
437
    {
438
        $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
439
        $stmt = $this->_conn->prepare($sql);
440
        $stmt->bindValue(1, new \DateTime('2010-01-01 10:10:10'), Type::DATETIME);
441
        $stmt->execute();
442
443
        self::assertEquals(1, $stmt->fetchColumn());
444
    }
445
446
    /**
447
     * @group DBAL-78
448
     */
449
    public function testNativeArrayListSupport()
450
    {
451
        for ($i = 100; $i < 110; $i++) {
452
            $this->_conn->insert('fetch_table', array('test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10'));
453
        }
454
455
        $stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_int IN (?)',
456
            array(array(100, 101, 102, 103, 104)), array(Connection::PARAM_INT_ARRAY));
457
458
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
459
        self::assertCount(5, $data);
460
        self::assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $data);
461
462
        $stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_string IN (?)',
463
            array(array('foo100', 'foo101', 'foo102', 'foo103', 'foo104')), array(Connection::PARAM_STR_ARRAY));
464
465
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
466
        self::assertCount(5, $data);
467
        self::assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $data);
468
    }
469
470
    /**
471
     * @dataProvider getTrimExpressionData
472
     */
473
    public function testTrimExpression($value, $position, $char, $expectedResult)
474
    {
475
        $sql = 'SELECT ' .
476
            $this->_conn->getDatabasePlatform()->getTrimExpression($value, $position, $char) . ' AS trimmed ' .
477
            'FROM fetch_table';
478
479
        $row = $this->_conn->fetchAssoc($sql);
480
        $row = array_change_key_case($row, CASE_LOWER);
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type boolean; 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

480
        $row = array_change_key_case(/** @scrutinizer ignore-type */ $row, CASE_LOWER);
Loading history...
481
482
        self::assertEquals($expectedResult, $row['trimmed']);
483
    }
484
485
    public function getTrimExpressionData()
486
    {
487
        return array(
488
            ['test_string', TrimMode::UNSPECIFIED, false, 'foo'],
489
            ['test_string', TrimMode::LEADING, false, 'foo'],
490
            ['test_string', TrimMode::TRAILING, false, 'foo'],
491
            ['test_string', TrimMode::BOTH, false, 'foo'],
492
            ['test_string', TrimMode::UNSPECIFIED, "'f'", 'oo'],
493
            ['test_string', TrimMode::UNSPECIFIED, "'o'", 'f'],
494
            ['test_string', TrimMode::UNSPECIFIED, "'.'", 'foo'],
495
            ['test_string', TrimMode::LEADING, "'f'", 'oo'],
496
            ['test_string', TrimMode::LEADING, "'o'", 'foo'],
497
            ['test_string', TrimMode::LEADING, "'.'", 'foo'],
498
            ['test_string', TrimMode::TRAILING, "'f'", 'foo'],
499
            ['test_string', TrimMode::TRAILING, "'o'", 'f'],
500
            ['test_string', TrimMode::TRAILING, "'.'", 'foo'],
501
            ['test_string', TrimMode::BOTH, "'f'", 'oo'],
502
            ['test_string', TrimMode::BOTH, "'o'", 'f'],
503
            ['test_string', TrimMode::BOTH, "'.'", 'foo'],
504
            ["' foo '", TrimMode::UNSPECIFIED, false, 'foo'],
505
            ["' foo '", TrimMode::LEADING, false, 'foo '],
506
            ["' foo '", TrimMode::TRAILING, false, ' foo'],
507
            ["' foo '", TrimMode::BOTH, false, 'foo'],
508
            ["' foo '", TrimMode::UNSPECIFIED, "'f'", ' foo '],
509
            ["' foo '", TrimMode::UNSPECIFIED, "'o'", ' foo '],
510
            ["' foo '", TrimMode::UNSPECIFIED, "'.'", ' foo '],
511
            ["' foo '", TrimMode::UNSPECIFIED, "' '", 'foo'],
512
            ["' foo '", TrimMode::LEADING, "'f'", ' foo '],
513
            ["' foo '", TrimMode::LEADING, "'o'", ' foo '],
514
            ["' foo '", TrimMode::LEADING, "'.'", ' foo '],
515
            ["' foo '", TrimMode::LEADING, "' '", 'foo '],
516
            ["' foo '", TrimMode::TRAILING, "'f'", ' foo '],
517
            ["' foo '", TrimMode::TRAILING, "'o'", ' foo '],
518
            ["' foo '", TrimMode::TRAILING, "'.'", ' foo '],
519
            ["' foo '", TrimMode::TRAILING, "' '", ' foo'],
520
            ["' foo '", TrimMode::BOTH, "'f'", ' foo '],
521
            ["' foo '", TrimMode::BOTH, "'o'", ' foo '],
522
            ["' foo '", TrimMode::BOTH, "'.'", ' foo '],
523
            ["' foo '", TrimMode::BOTH, "' '", 'foo'],
524
        );
525
    }
526
527
    /**
528
     * @group DDC-1014
529
     */
530
    public function testDateArithmetics()
531
    {
532
        $p = $this->_conn->getDatabasePlatform();
533
        $sql = 'SELECT ';
534
        $sql .= $p->getDateDiffExpression($p->getCurrentTimestampSQL(), 'test_datetime') . ' AS diff, ';
535
        $sql .= $p->getDateAddSecondsExpression('test_datetime', 1) .' AS add_seconds, ';
536
        $sql .= $p->getDateSubSecondsExpression('test_datetime', 1) .' AS sub_seconds, ';
537
        $sql .= $p->getDateAddMinutesExpression('test_datetime', 5) .' AS add_minutes, ';
538
        $sql .= $p->getDateSubMinutesExpression('test_datetime', 5) .' AS sub_minutes, ';
539
        $sql .= $p->getDateAddHourExpression('test_datetime', 3) .' AS add_hour, ';
540
        $sql .= $p->getDateSubHourExpression('test_datetime', 3) .' AS sub_hour, ';
541
        $sql .= $p->getDateAddDaysExpression('test_datetime', 10) .' AS add_days, ';
542
        $sql .= $p->getDateSubDaysExpression('test_datetime', 10) .' AS sub_days, ';
543
        $sql .= $p->getDateAddWeeksExpression('test_datetime', 1) .' AS add_weeks, ';
544
        $sql .= $p->getDateSubWeeksExpression('test_datetime', 1) .' AS sub_weeks, ';
545
        $sql .= $p->getDateAddMonthExpression('test_datetime', 2) .' AS add_month, ';
546
        $sql .= $p->getDateSubMonthExpression('test_datetime', 2) .' AS sub_month, ';
547
        $sql .= $p->getDateAddQuartersExpression('test_datetime', 3) .' AS add_quarters, ';
548
        $sql .= $p->getDateSubQuartersExpression('test_datetime', 3) .' AS sub_quarters, ';
549
        $sql .= $p->getDateAddYearsExpression('test_datetime', 6) .' AS add_years, ';
550
        $sql .= $p->getDateSubYearsExpression('test_datetime', 6) .' AS sub_years ';
551
        $sql .= 'FROM fetch_table';
552
553
        $row = $this->_conn->fetchAssoc($sql);
554
        $row = array_change_key_case($row, CASE_LOWER);
555
556
        $diff = (new \DateTime('today'))->diff(new \DateTime('2010-01-01'))->days;
557
        self::assertEquals($diff, $row['diff'], 'Date difference should be ' . $diff . ' days.');
558
        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");
559
        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");
560
        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");
561
        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");
562
        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");
563
        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");
564
        self::assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), "Adding date should end up on 2010-01-11");
565
        self::assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), "Subtracting date should end up on 2009-12-22");
566
        self::assertEquals('2010-01-08', date('Y-m-d', strtotime($row['add_weeks'])), "Adding week should end up on 2010-01-08");
567
        self::assertEquals('2009-12-25', date('Y-m-d', strtotime($row['sub_weeks'])), "Subtracting week should end up on 2009-12-25");
568
        self::assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), "Adding month should end up on 2010-03-01");
569
        self::assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), "Subtracting month should end up on 2009-11-01");
570
        self::assertEquals('2010-10-01', date('Y-m-d', strtotime($row['add_quarters'])), "Adding quarters should end up on 2010-04-01");
571
        self::assertEquals('2009-04-01', date('Y-m-d', strtotime($row['sub_quarters'])), "Subtracting quarters should end up on 2009-10-01");
572
        self::assertEquals('2016-01-01', date('Y-m-d', strtotime($row['add_years'])), "Adding years should end up on 2016-01-01");
573
        self::assertEquals('2004-01-01', date('Y-m-d', strtotime($row['sub_years'])), "Subtracting years should end up on 2004-01-01");
574
    }
575
576
    /**
577
     * @dataProvider dateDiffProvider
578
     */
579
    public function testDateDiff(string $date1, string $date2, int $expected)
580
    {
581
        $platform = $this->_conn->getDatabasePlatform();
582
583
        $sql  = str_replace(
584
            '1',
585
            $platform->getDateDiffExpression('?', '?'),
586
            $platform->getDummySelectSQL()
587
        );
588
        $diff = $this->_conn->executeQuery($sql, [$date1, $date2])->fetchColumn();
589
590
        self::assertEquals($expected, $diff);
591
    }
592
593
    public static function dateDiffProvider()
594
    {
595
        return [
596
            'same day' => ['2018-04-14 23:59:59', '2018-04-14 00:00:00', 0],
597
            'midnight' => ['2018-04-14 00:00:00', '2018-04-13 23:59:59', 1],
598
        ];
599
    }
600
601
    public function testLocateExpression()
602
    {
603
        $platform = $this->_conn->getDatabasePlatform();
604
605
        $sql = 'SELECT ';
606
        $sql .= $platform->getLocateExpression('test_string', "'oo'") .' AS locate1, ';
607
        $sql .= $platform->getLocateExpression('test_string', "'foo'") .' AS locate2, ';
608
        $sql .= $platform->getLocateExpression('test_string', "'bar'") .' AS locate3, ';
609
        $sql .= $platform->getLocateExpression('test_string', 'test_string') .' AS locate4, ';
610
        $sql .= $platform->getLocateExpression("'foo'", 'test_string') .' AS locate5, ';
611
        $sql .= $platform->getLocateExpression("'barfoobaz'", 'test_string') .' AS locate6, ';
612
        $sql .= $platform->getLocateExpression("'bar'", 'test_string') .' AS locate7, ';
613
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 2) .' AS locate8, ';
614
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 3) .' AS locate9 ';
615
        $sql .= 'FROM fetch_table';
616
617
        $row = $this->_conn->fetchAssoc($sql);
618
        $row = array_change_key_case($row, CASE_LOWER);
619
620
        self::assertEquals(2, $row['locate1']);
621
        self::assertEquals(1, $row['locate2']);
622
        self::assertEquals(0, $row['locate3']);
623
        self::assertEquals(1, $row['locate4']);
624
        self::assertEquals(1, $row['locate5']);
625
        self::assertEquals(4, $row['locate6']);
626
        self::assertEquals(0, $row['locate7']);
627
        self::assertEquals(2, $row['locate8']);
628
        self::assertEquals(0, $row['locate9']);
629
    }
630
631
    public function testQuoteSQLInjection()
632
    {
633
        $sql = "SELECT * FROM fetch_table WHERE test_string = " . $this->_conn->quote("bar' OR '1'='1");
634
        $rows = $this->_conn->fetchAll($sql);
635
636
        self::assertCount(0, $rows, "no result should be returned, otherwise SQL injection is possible");
637
    }
638
639
    /**
640
     * @group DDC-1213
641
     */
642
    public function testBitComparisonExpressionSupport()
643
    {
644
        $this->_conn->exec('DELETE FROM fetch_table');
645
        $platform = $this->_conn->getDatabasePlatform();
646
        $bitmap   = array();
647
648
        for ($i = 2; $i < 9; $i = $i + 2) {
649
            $bitmap[$i] = array(
650
                'bit_or'    => ($i | 2),
651
                'bit_and'   => ($i & 2)
652
            );
653
            $this->_conn->insert('fetch_table', array(
654
                'test_int'      => $i,
655
                'test_string'   => json_encode($bitmap[$i]),
656
                'test_datetime' => '2010-01-01 10:10:10'
657
            ));
658
        }
659
660
        $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...
661
        $sql[]  = 'test_int, ';
662
        $sql[]  = 'test_string, ';
663
        $sql[]  = $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or, ';
664
        $sql[]  = $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and ';
665
        $sql[]  = 'FROM fetch_table';
666
667
        $stmt = $this->_conn->executeQuery(implode(PHP_EOL, $sql));
668
        $data = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
669
670
671
        self::assertCount(4, $data);
672
        self::assertEquals(count($bitmap), count($data));
673
        foreach ($data as $row) {
674
            $row = array_change_key_case($row, CASE_LOWER);
675
676
            self::assertArrayHasKey('test_int', $row);
677
678
            $id = $row['test_int'];
679
680
            self::assertArrayHasKey($id, $bitmap);
681
            self::assertArrayHasKey($id, $bitmap);
682
683
            self::assertArrayHasKey('bit_or', $row);
684
            self::assertArrayHasKey('bit_and', $row);
685
686
            self::assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
687
            self::assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
688
        }
689
    }
690
691
    public function testSetDefaultFetchMode()
692
    {
693
        $stmt = $this->_conn->query("SELECT * FROM fetch_table");
694
        $stmt->setFetchMode(FetchMode::NUMERIC);
695
696
        $row = array_keys($stmt->fetch());
697
        self::assertCount(0, array_filter($row, function($v) { return ! is_numeric($v); }), "should be no non-numerical elements in the result.");
698
    }
699
700
    /**
701
     * @group DBAL-1091
702
     */
703
    public function testFetchAllStyleObject()
704
    {
705
        $this->setupFixture();
706
707
        $sql = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
708
        $stmt = $this->_conn->prepare($sql);
709
710
        $stmt->execute();
711
712
        $results = $stmt->fetchAll(FetchMode::STANDARD_OBJECT);
713
714
        self::assertCount(1, $results);
715
        self::assertInstanceOf('stdClass', $results[0]);
716
717
        self::assertEquals(
718
            1,
719
            property_exists($results[0], 'test_int') ? $results[0]->test_int : $results[0]->TEST_INT
720
        );
721
        self::assertEquals(
722
            'foo',
723
            property_exists($results[0], 'test_string') ? $results[0]->test_string : $results[0]->TEST_STRING
724
        );
725
        self::assertStringStartsWith(
726
            '2010-01-01 10:10:10',
727
            property_exists($results[0], 'test_datetime') ? $results[0]->test_datetime : $results[0]->TEST_DATETIME
728
        );
729
    }
730
731
    /**
732
     * @group DBAL-196
733
     */
734
    public function testFetchAllSupportFetchClass()
735
    {
736
        $this->skipOci8AndMysqli();
737
        $this->setupFixture();
738
739
        $sql    = "SELECT test_int, test_string, test_datetime FROM fetch_table";
740
        $stmt   = $this->_conn->prepare($sql);
741
        $stmt->execute();
742
743
        $results = $stmt->fetchAll(
744
            FetchMode::CUSTOM_OBJECT,
745
            __NAMESPACE__.'\\MyFetchClass'
746
        );
747
748
        self::assertCount(1, $results);
749
        self::assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]);
750
751
        self::assertEquals(1, $results[0]->test_int);
752
        self::assertEquals('foo', $results[0]->test_string);
753
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
754
    }
755
756
    /**
757
     * @group DBAL-241
758
     */
759
    public function testFetchAllStyleColumn()
760
    {
761
        $sql = "DELETE FROM fetch_table";
762
        $this->_conn->executeUpdate($sql);
763
764
        $this->_conn->insert('fetch_table', array('test_int' => 1, 'test_string' => 'foo'));
765
        $this->_conn->insert('fetch_table', array('test_int' => 10, 'test_string' => 'foo'));
766
767
        $sql = "SELECT test_int FROM fetch_table";
768
        $rows = $this->_conn->query($sql)->fetchAll(FetchMode::COLUMN);
769
770
        self::assertEquals(array(1, 10), $rows);
771
    }
772
773
    /**
774
     * @group DBAL-214
775
     */
776
    public function testSetFetchModeClassFetchAll()
777
    {
778
        $this->skipOci8AndMysqli();
779
        $this->setupFixture();
780
781
        $sql = "SELECT * FROM fetch_table";
782
        $stmt = $this->_conn->query($sql);
783
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, __NAMESPACE__ . '\\MyFetchClass');
784
785
        $results = $stmt->fetchAll();
786
787
        self::assertCount(1, $results);
788
        self::assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]);
789
790
        self::assertEquals(1, $results[0]->test_int);
791
        self::assertEquals('foo', $results[0]->test_string);
792
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
793
    }
794
795
    /**
796
     * @group DBAL-214
797
     */
798
    public function testSetFetchModeClassFetch()
799
    {
800
        $this->skipOci8AndMysqli();
801
        $this->setupFixture();
802
803
        $sql = "SELECT * FROM fetch_table";
804
        $stmt = $this->_conn->query($sql);
805
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, __NAMESPACE__ . '\\MyFetchClass');
806
807
        $results = array();
808
        while ($row = $stmt->fetch()) {
809
            $results[] = $row;
810
        }
811
812
        self::assertCount(1, $results);
813
        self::assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]);
814
815
        self::assertEquals(1, $results[0]->test_int);
816
        self::assertEquals('foo', $results[0]->test_string);
817
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
818
    }
819
820
    /**
821
     * @group DBAL-257
822
     */
823
    public function testEmptyFetchColumnReturnsFalse()
824
    {
825
        $this->_conn->beginTransaction();
826
        $this->_conn->exec('DELETE FROM fetch_table');
827
        self::assertFalse($this->_conn->fetchColumn('SELECT test_int FROM fetch_table'));
828
        self::assertFalse($this->_conn->query('SELECT test_int FROM fetch_table')->fetchColumn());
829
        $this->_conn->rollBack();
830
    }
831
832
    /**
833
     * @group DBAL-339
834
     */
835
    public function testSetFetchModeOnDbalStatement()
836
    {
837
        $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
838
        $stmt = $this->_conn->executeQuery($sql, array(1, "foo"));
839
        $stmt->setFetchMode(FetchMode::NUMERIC);
840
841
        $row = $stmt->fetch();
842
843
        self::assertArrayHasKey(0, $row);
844
        self::assertArrayHasKey(1, $row);
845
        self::assertFalse($stmt->fetch());
846
    }
847
848
    /**
849
     * @group DBAL-435
850
     */
851
    public function testEmptyParameters()
852
    {
853
        $sql = "SELECT * FROM fetch_table WHERE test_int IN (?)";
854
        $stmt = $this->_conn->executeQuery($sql, array(array()), array(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY));
855
        $rows = $stmt->fetchAll();
856
857
        self::assertEquals(array(), $rows);
858
    }
859
860
    /**
861
     * @group DBAL-1028
862
     */
863
    public function testFetchColumnNullValue()
864
    {
865
        $this->_conn->executeUpdate(
866
            'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)',
867
            array(2, 'foo')
868
        );
869
870
        self::assertNull(
871
            $this->_conn->fetchColumn('SELECT test_datetime FROM fetch_table WHERE test_int = ?', array(2))
872
        );
873
    }
874
875
    /**
876
     * @group DBAL-1028
877
     */
878
    public function testFetchColumnNonExistingIndex()
879
    {
880
        if ($this->_conn->getDriver()->getName() === 'pdo_sqlsrv') {
881
            $this->markTestSkipped(
882
                'Test does not work for pdo_sqlsrv driver as it throws a fatal error for a non-existing column index.'
883
            );
884
        }
885
886
        self::assertNull(
887
            $this->_conn->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', array(1), 1)
888
        );
889
    }
890
891
    /**
892
     * @group DBAL-1028
893
     */
894
    public function testFetchColumnNoResult()
895
    {
896
        self::assertFalse(
897
            $this->_conn->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', array(-1))
898
        );
899
    }
900
901
    private function setupFixture()
902
    {
903
        $this->_conn->exec('DELETE FROM fetch_table');
904
        $this->_conn->insert('fetch_table', array(
905
            'test_int'      => 1,
906
            'test_string'   => 'foo',
907
            'test_datetime' => '2010-01-01 10:10:10'
908
        ));
909
    }
910
911
    private function skipOci8AndMysqli()
912
    {
913
        if (isset($GLOBALS['db_type']) && $GLOBALS['db_type'] == "oci8")  {
914
            $this->markTestSkipped("Not supported by OCI8");
915
        }
916
        if ('mysqli' == $this->_conn->getDriver()->getName()) {
917
            $this->markTestSkipped('Mysqli driver dont support this feature.');
918
        }
919
    }
920
}
921
922
class MyFetchClass
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
923
{
924
    public $test_int, $test_string, $test_datetime;
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
925
}
926