DataAccessTest.php 34.6 KB
Newer Older
1 2
<?php

3 4
namespace Doctrine\Tests\DBAL\Functional;

Sergei Morozov's avatar
Sergei Morozov committed
5
use DateTime;
jeroendedauw's avatar
jeroendedauw committed
6
use Doctrine\DBAL\Connection;
7
use Doctrine\DBAL\DBALException;
8
use Doctrine\DBAL\Driver\IBMDB2\Driver as IBMDB2Driver;
Sergei Morozov's avatar
Sergei Morozov committed
9
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
10
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver;
11
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
12
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
Sergei Morozov's avatar
Sergei Morozov committed
13
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
14 15
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
16
use Doctrine\DBAL\Platforms\SqlitePlatform;
17
use Doctrine\DBAL\Platforms\TrimMode;
18
use Doctrine\DBAL\Schema\Table;
Sergei Morozov's avatar
Sergei Morozov committed
19
use Doctrine\DBAL\Statement;
20
use Doctrine\DBAL\Types\Types;
Sergei Morozov's avatar
Sergei Morozov committed
21
use Doctrine\Tests\DbalFunctionalTestCase;
22
use PDO;
23

24 25 26 27 28 29 30 31 32
use function array_change_key_case;
use function array_filter;
use function array_keys;
use function count;
use function date;
use function implode;
use function is_numeric;
use function json_encode;
use function property_exists;
Sergei Morozov's avatar
Sergei Morozov committed
33
use function sprintf;
34
use function strtotime;
35

Grégoire Paris's avatar
Grégoire Paris committed
36 37
use const CASE_LOWER;
use const PHP_EOL;
38

Sergei Morozov's avatar
Sergei Morozov committed
39
class DataAccessTest extends DbalFunctionalTestCase
40
{
Sergei Morozov's avatar
Sergei Morozov committed
41
    /** @var bool */
42
    private static $generated = false;
43

44
    protected function setUp(): void
45 46 47
    {
        parent::setUp();

Sergei Morozov's avatar
Sergei Morozov committed
48 49 50
        if (self::$generated !== false) {
            return;
        }
51

Sergei Morozov's avatar
Sergei Morozov committed
52 53 54 55 56
        $table = new Table('fetch_table');
        $table->addColumn('test_int', 'integer');
        $table->addColumn('test_string', 'string');
        $table->addColumn('test_datetime', 'datetime', ['notnull' => false]);
        $table->setPrimaryKey(['test_int']);
57

Sergei Morozov's avatar
Sergei Morozov committed
58
        $sm = $this->connection->getSchemaManager();
Sergei Morozov's avatar
Sergei Morozov committed
59 60
        $sm->createTable($table);

Sergei Morozov's avatar
Sergei Morozov committed
61
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo', 'test_datetime' => '2010-01-01 10:10:10']);
Sergei Morozov's avatar
Sergei Morozov committed
62
        self::$generated = true;
63 64
    }

65
    public function testPrepareWithBindValue(): void
66
    {
Sergei Morozov's avatar
Sergei Morozov committed
67
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
68 69
        $stmt = $this->connection->prepare($sql);
        self::assertInstanceOf(Statement::class, $stmt);
70 71 72 73 74

        $stmt->bindValue(1, 1);
        $stmt->bindValue(2, 'foo');
        $stmt->execute();

75
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
Sergei Morozov's avatar
Sergei Morozov committed
76 77
        $row = array_change_key_case($row, CASE_LOWER);
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
78 79
    }

80
    public function testPrepareWithBindParam(): void
81 82 83 84
    {
        $paramInt = 1;
        $paramStr = 'foo';

Sergei Morozov's avatar
Sergei Morozov committed
85
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
86 87
        $stmt = $this->connection->prepare($sql);
        self::assertInstanceOf(Statement::class, $stmt);
88 89 90 91 92

        $stmt->bindParam(1, $paramInt);
        $stmt->bindParam(2, $paramStr);
        $stmt->execute();

93
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
Sergei Morozov's avatar
Sergei Morozov committed
94 95
        $row = array_change_key_case($row, CASE_LOWER);
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
96 97
    }

98
    public function testPrepareWithFetchAll(): void
99 100 101 102
    {
        $paramInt = 1;
        $paramStr = 'foo';

Sergei Morozov's avatar
Sergei Morozov committed
103
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
104 105
        $stmt = $this->connection->prepare($sql);
        self::assertInstanceOf(Statement::class, $stmt);
106 107 108 109 110

        $stmt->bindParam(1, $paramInt);
        $stmt->bindParam(2, $paramStr);
        $stmt->execute();

111
        $rows    = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
Sergei Morozov's avatar
Sergei Morozov committed
112 113
        $rows[0] = array_change_key_case($rows[0], CASE_LOWER);
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
114 115
    }

116 117 118
    /**
     * @group DBAL-228
     */
119
    public function testPrepareWithFetchAllBoth(): void
120 121 122 123
    {
        $paramInt = 1;
        $paramStr = 'foo';

Sergei Morozov's avatar
Sergei Morozov committed
124
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
125 126
        $stmt = $this->connection->prepare($sql);
        self::assertInstanceOf(Statement::class, $stmt);
127 128 129 130 131

        $stmt->bindParam(1, $paramInt);
        $stmt->bindParam(2, $paramStr);
        $stmt->execute();

132
        $rows    = $stmt->fetchAll(FetchMode::MIXED);
Sergei Morozov's avatar
Sergei Morozov committed
133 134
        $rows[0] = array_change_key_case($rows[0], CASE_LOWER);
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo', 0 => 1, 1 => 'foo'], $rows[0]);
135 136
    }

137
    public function testPrepareWithFetchColumn(): void
138 139 140 141
    {
        $paramInt = 1;
        $paramStr = 'foo';

Sergei Morozov's avatar
Sergei Morozov committed
142
        $sql  = 'SELECT test_int FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
143 144
        $stmt = $this->connection->prepare($sql);
        self::assertInstanceOf(Statement::class, $stmt);
145 146 147 148 149 150

        $stmt->bindParam(1, $paramInt);
        $stmt->bindParam(2, $paramStr);
        $stmt->execute();

        $column = $stmt->fetchColumn();
151
        self::assertEquals(1, $column);
152 153
    }

154
    public function testPrepareWithIterator(): void
155 156 157 158
    {
        $paramInt = 1;
        $paramStr = 'foo';

Sergei Morozov's avatar
Sergei Morozov committed
159
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
160 161
        $stmt = $this->connection->prepare($sql);
        self::assertInstanceOf(Statement::class, $stmt);
162 163 164 165 166

        $stmt->bindParam(1, $paramInt);
        $stmt->bindParam(2, $paramStr);
        $stmt->execute();

Sergei Morozov's avatar
Sergei Morozov committed
167
        $rows = [];
168
        $stmt->setFetchMode(FetchMode::ASSOCIATIVE);
169
        foreach ($stmt as $row) {
Sergei Morozov's avatar
Sergei Morozov committed
170
            $rows[] = array_change_key_case($row, CASE_LOWER);
171 172
        }

Sergei Morozov's avatar
Sergei Morozov committed
173
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $rows[0]);
174 175
    }

176
    public function testPrepareWithQuoted(): void
177
    {
Sergei Morozov's avatar
Sergei Morozov committed
178
        $table    = 'fetch_table';
179 180 181
        $paramInt = 1;
        $paramStr = 'foo';

Sergei Morozov's avatar
Sergei Morozov committed
182 183 184 185 186 187 188
        $stmt = $this->connection->prepare(sprintf(
            'SELECT test_int, test_string FROM %s WHERE test_int = %s AND test_string = %s',
            $this->connection->quoteIdentifier($table),
            $this->connection->quote($paramInt),
            $this->connection->quote($paramStr)
        ));
        self::assertInstanceOf(Statement::class, $stmt);
189 190
    }

191
    public function testPrepareWithExecuteParams(): void
192 193 194 195
    {
        $paramInt = 1;
        $paramStr = 'foo';

Sergei Morozov's avatar
Sergei Morozov committed
196
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
197 198
        $stmt = $this->connection->prepare($sql);
        self::assertInstanceOf(Statement::class, $stmt);
Sergei Morozov's avatar
Sergei Morozov committed
199
        $stmt->execute([$paramInt, $paramStr]);
200

201
        $row = $stmt->fetch(FetchMode::ASSOCIATIVE);
Gabriel Caruso's avatar
Gabriel Caruso committed
202
        self::assertNotFalse($row);
Sergei Morozov's avatar
Sergei Morozov committed
203 204
        $row = array_change_key_case($row, CASE_LOWER);
        self::assertEquals(['test_int' => 1, 'test_string' => 'foo'], $row);
205 206
    }

207
    public function testFetchAll(): void
208
    {
Sergei Morozov's avatar
Sergei Morozov committed
209
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
210
        $data = $this->connection->fetchAll($sql, [1, 'foo']);
211

Gabriel Caruso's avatar
Gabriel Caruso committed
212
        self::assertCount(1, $data);
213 214

        $row = $data[0];
Gabriel Caruso's avatar
Gabriel Caruso committed
215
        self::assertCount(2, $row);
216

Sergei Morozov's avatar
Sergei Morozov committed
217
        $row = array_change_key_case($row, CASE_LOWER);
218 219
        self::assertEquals(1, $row['test_int']);
        self::assertEquals('foo', $row['test_string']);
220 221
    }

root's avatar
root committed
222 223 224
    /**
     * @group DBAL-209
     */
225
    public function testFetchAllWithTypes(): void
root's avatar
root committed
226 227
    {
        $datetimeString = '2010-01-01 10:10:10';
Sergei Morozov's avatar
Sergei Morozov committed
228
        $datetime       = new DateTime($datetimeString);
229

230
        $sql  = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
231 232 233 234 235
        $data = $this->connection->fetchAll(
            $sql,
            [1, $datetime],
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
        );
root's avatar
root committed
236

Gabriel Caruso's avatar
Gabriel Caruso committed
237
        self::assertCount(1, $data);
root's avatar
root committed
238 239

        $row = $data[0];
Gabriel Caruso's avatar
Gabriel Caruso committed
240
        self::assertCount(2, $row);
root's avatar
root committed
241

Sergei Morozov's avatar
Sergei Morozov committed
242
        $row = array_change_key_case($row, CASE_LOWER);
243 244
        self::assertEquals(1, $row['test_int']);
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
root's avatar
root committed
245
    }
246

root's avatar
root committed
247 248
    /**
     * @group DBAL-209
249
     * @dataProvider fetchProvider
root's avatar
root committed
250
     */
251
    public function testFetchAllWithMissingTypes(callable $fetch): void
root's avatar
root committed
252
    {
253 254 255 256
        if (
            $this->connection->getDriver() instanceof MySQLiDriver ||
            $this->connection->getDriver() instanceof SQLSrvDriver
        ) {
Steve Müller's avatar
Steve Müller committed
257
            $this->markTestSkipped('mysqli and sqlsrv actually supports this');
258 259
        }

260
        if (
261
            $this->connection->getDriver() instanceof IBMDB2Driver
262 263 264 265 266 267
        ) {
            $this->markTestSkipped(
                'ibm_ibm2 may or may not report the error depending on the PHP version and the connection state'
            );
        }

root's avatar
root committed
268
        $datetimeString = '2010-01-01 10:10:10';
Sergei Morozov's avatar
Sergei Morozov committed
269 270
        $datetime       = new DateTime($datetimeString);
        $sql            = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
271 272 273

        $this->expectException(DBALException::class);

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
        $fetch($this->connection, $sql, [1, $datetime]);
    }

    /**
     * @return iterable<string,array{0:callable}>
     */
    public static function fetchProvider(): iterable
    {
        yield 'fetch-all-associative' => [
            static function (Connection $connection, string $query, array $params): void {
                $connection->fetchAll($query, $params);
            },
        ];

        yield 'fetch-numeric' => [
            static function (Connection $connection, string $query, array $params): void {
                $connection->fetchArray($query, $params);
            },
        ];

        yield 'fetch-associative' => [
            static function (Connection $connection, string $query, array $params): void {
                $connection->fetchAssoc($query, $params);
            },
        ];

        yield 'fetch-one' => [
            static function (Connection $connection, string $query, array $params): void {
                $connection->fetchColumn($query, $params);
            },
        ];
root's avatar
root committed
305 306
    }

307
    public function testFetchBoth(): void
308
    {
309
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
310
        $row = $this->connection->executeQuery($sql, [1, 'foo'])->fetch(FetchMode::MIXED);
311

Gabriel Caruso's avatar
Gabriel Caruso committed
312
        self::assertNotFalse($row);
313

Sergei Morozov's avatar
Sergei Morozov committed
314
        $row = array_change_key_case($row, CASE_LOWER);
315

316 317 318 319
        self::assertEquals(1, $row['test_int']);
        self::assertEquals('foo', $row['test_string']);
        self::assertEquals(1, $row[0]);
        self::assertEquals('foo', $row[1]);
320 321
    }

322
    public function testFetchNoResult(): void
323
    {
324
        self::assertFalse(
Sergei Morozov's avatar
Sergei Morozov committed
325
            $this->connection->executeQuery('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])->fetch()
326
        );
327 328
    }

329
    public function testFetchAssoc(): void
330
    {
Sergei Morozov's avatar
Sergei Morozov committed
331
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
332
        $row = $this->connection->fetchAssoc($sql, [1, 'foo']);
333

Gabriel Caruso's avatar
Gabriel Caruso committed
334
        self::assertNotFalse($row);
335

Sergei Morozov's avatar
Sergei Morozov committed
336
        $row = array_change_key_case($row, CASE_LOWER);
337

338 339
        self::assertEquals(1, $row['test_int']);
        self::assertEquals('foo', $row['test_string']);
340 341
    }

342
    public function testFetchAssocWithTypes(): void
343 344
    {
        $datetimeString = '2010-01-01 10:10:10';
Sergei Morozov's avatar
Sergei Morozov committed
345
        $datetime       = new DateTime($datetimeString);
346

347
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
348 349 350 351 352
        $row = $this->connection->fetchAssoc(
            $sql,
            [1, $datetime],
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
        );
353

Gabriel Caruso's avatar
Gabriel Caruso committed
354
        self::assertNotFalse($row);
355

Sergei Morozov's avatar
Sergei Morozov committed
356
        $row = array_change_key_case($row, CASE_LOWER);
357

358 359
        self::assertEquals(1, $row['test_int']);
        self::assertStringStartsWith($datetimeString, $row['test_datetime']);
360 361
    }

362
    public function testFetchArray(): void
363
    {
Sergei Morozov's avatar
Sergei Morozov committed
364
        $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
365
        $row = $this->connection->fetchArray($sql, [1, 'foo']);
366

367 368
        self::assertEquals(1, $row[0]);
        self::assertEquals('foo', $row[1]);
369 370
    }

371
    public function testFetchArrayWithTypes(): void
372 373
    {
        $datetimeString = '2010-01-01 10:10:10';
Sergei Morozov's avatar
Sergei Morozov committed
374
        $datetime       = new DateTime($datetimeString);
375

376
        $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
377 378 379 380 381
        $row = $this->connection->fetchArray(
            $sql,
            [1, $datetime],
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
        );
382

Gabriel Caruso's avatar
Gabriel Caruso committed
383
        self::assertNotFalse($row);
384

Sergei Morozov's avatar
Sergei Morozov committed
385
        $row = array_change_key_case($row, CASE_LOWER);
386

387 388
        self::assertEquals(1, $row[0]);
        self::assertStringStartsWith($datetimeString, $row[1]);
389 390
    }

391
    public function testFetchColumn(): void
392
    {
Sergei Morozov's avatar
Sergei Morozov committed
393
        $sql     = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
394
        $testInt = $this->connection->fetchColumn($sql, [1, 'foo'], 0);
395

396
        self::assertEquals(1, $testInt);
397

Sergei Morozov's avatar
Sergei Morozov committed
398
        $sql        = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
399
        $testString = $this->connection->fetchColumn($sql, [1, 'foo'], 1);
400

401
        self::assertEquals('foo', $testString);
402
    }
403

404
    public function testFetchColumnWithTypes(): void
405 406
    {
        $datetimeString = '2010-01-01 10:10:10';
Sergei Morozov's avatar
Sergei Morozov committed
407
        $datetime       = new DateTime($datetimeString);
408

409
        $sql    = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
410 411 412 413 414 415
        $column = $this->connection->fetchColumn(
            $sql,
            [1, $datetime],
            1,
            [ParameterType::STRING, Types::DATETIME_MUTABLE]
        );
416

Gabriel Caruso's avatar
Gabriel Caruso committed
417
        self::assertNotFalse($column);
418

419
        self::assertStringStartsWith($datetimeString, $column);
420 421
    }

422 423 424
    /**
     * @group DDC-697
     */
425
    public function testExecuteQueryBindDateTimeType(): void
426
    {
Sergei Morozov's avatar
Sergei Morozov committed
427
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
Sergei Morozov's avatar
Sergei Morozov committed
428
        $stmt = $this->connection->executeQuery(
Sergei Morozov's avatar
Sergei Morozov committed
429 430
            $sql,
            [1 => new DateTime('2010-01-01 10:10:10')],
431
            [1 => Types::DATETIME_MUTABLE]
432 433
        );

434
        self::assertEquals(1, $stmt->fetchColumn());
435 436 437 438 439
    }

    /**
     * @group DDC-697
     */
440
    public function testExecuteUpdateBindDateTimeType(): void
441
    {
Sergei Morozov's avatar
Sergei Morozov committed
442
        $datetime = new DateTime('2010-02-02 20:20:20');
443

Sergei Morozov's avatar
Sergei Morozov committed
444
        $sql          = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)';
Sergei Morozov's avatar
Sergei Morozov committed
445
        $affectedRows = $this->connection->executeUpdate($sql, [
446 447 448 449 450 451
            1 => 50,
            2 => 'foo',
            3 => $datetime,
        ], [
            1 => ParameterType::INTEGER,
            2 => ParameterType::STRING,
452
            3 => Types::DATETIME_MUTABLE,
453
        ]);
454

455
        self::assertEquals(1, $affectedRows);
Sergei Morozov's avatar
Sergei Morozov committed
456
        self::assertEquals(1, $this->connection->executeQuery(
457
            'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
Sergei Morozov's avatar
Sergei Morozov committed
458
            [1 => $datetime],
459
            [1 => Types::DATETIME_MUTABLE]
460 461 462 463 464 465
        )->fetchColumn());
    }

    /**
     * @group DDC-697
     */
466
    public function testPrepareQueryBindValueDateTimeType(): void
467
    {
Sergei Morozov's avatar
Sergei Morozov committed
468
        $sql  = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
Sergei Morozov's avatar
Sergei Morozov committed
469
        $stmt = $this->connection->prepare($sql);
470
        $stmt->bindValue(1, new DateTime('2010-01-01 10:10:10'), Types::DATETIME_MUTABLE);
471 472
        $stmt->execute();

473
        self::assertEquals(1, $stmt->fetchColumn());
474
    }
475

476 477 478
    /**
     * @group DBAL-78
     */
479
    public function testNativeArrayListSupport(): void
480 481
    {
        for ($i = 100; $i < 110; $i++) {
Sergei Morozov's avatar
Sergei Morozov committed
482
            $this->connection->insert('fetch_table', ['test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10']);
483
        }
484

Sergei Morozov's avatar
Sergei Morozov committed
485
        $stmt = $this->connection->executeQuery(
Sergei Morozov's avatar
Sergei Morozov committed
486 487 488 489
            'SELECT test_int FROM fetch_table WHERE test_int IN (?)',
            [[100, 101, 102, 103, 104]],
            [Connection::PARAM_INT_ARRAY]
        );
490

491
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
Gabriel Caruso's avatar
Gabriel Caruso committed
492
        self::assertCount(5, $data);
Sergei Morozov's avatar
Sergei Morozov committed
493
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
494

Sergei Morozov's avatar
Sergei Morozov committed
495
        $stmt = $this->connection->executeQuery(
Sergei Morozov's avatar
Sergei Morozov committed
496 497 498 499
            'SELECT test_int FROM fetch_table WHERE test_string IN (?)',
            [['foo100', 'foo101', 'foo102', 'foo103', 'foo104']],
            [Connection::PARAM_STR_ARRAY]
        );
500

501
        $data = $stmt->fetchAll(FetchMode::NUMERIC);
Gabriel Caruso's avatar
Gabriel Caruso committed
502
        self::assertCount(5, $data);
Sergei Morozov's avatar
Sergei Morozov committed
503
        self::assertEquals([[100], [101], [102], [103], [104]], $data);
504
    }
505

506
    /**
507 508
     * @param string|false $char
     *
509 510
     * @dataProvider getTrimExpressionData
     */
511
    public function testTrimExpression(string $value, int $position, $char, string $expectedResult): void
512 513
    {
        $sql = 'SELECT ' .
Sergei Morozov's avatar
Sergei Morozov committed
514
            $this->connection->getDatabasePlatform()->getTrimExpression($value, $position, $char) . ' AS trimmed ' .
515 516
            'FROM fetch_table';

Sergei Morozov's avatar
Sergei Morozov committed
517
        $row = $this->connection->fetchAssoc($sql);
518 519
        $row = array_change_key_case($row, CASE_LOWER);

520
        self::assertEquals($expectedResult, $row['trimmed']);
521 522
    }

523 524 525
    /**
     * @return array<int, array<int, mixed>>
     */
526
    public static function getTrimExpressionData(): iterable
527
    {
Sergei Morozov's avatar
Sergei Morozov committed
528
        return [
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
            ['test_string', TrimMode::UNSPECIFIED, false, 'foo'],
            ['test_string', TrimMode::LEADING, false, 'foo'],
            ['test_string', TrimMode::TRAILING, false, 'foo'],
            ['test_string', TrimMode::BOTH, false, 'foo'],
            ['test_string', TrimMode::UNSPECIFIED, "'f'", 'oo'],
            ['test_string', TrimMode::UNSPECIFIED, "'o'", 'f'],
            ['test_string', TrimMode::UNSPECIFIED, "'.'", 'foo'],
            ['test_string', TrimMode::LEADING, "'f'", 'oo'],
            ['test_string', TrimMode::LEADING, "'o'", 'foo'],
            ['test_string', TrimMode::LEADING, "'.'", 'foo'],
            ['test_string', TrimMode::TRAILING, "'f'", 'foo'],
            ['test_string', TrimMode::TRAILING, "'o'", 'f'],
            ['test_string', TrimMode::TRAILING, "'.'", 'foo'],
            ['test_string', TrimMode::BOTH, "'f'", 'oo'],
            ['test_string', TrimMode::BOTH, "'o'", 'f'],
            ['test_string', TrimMode::BOTH, "'.'", 'foo'],
            ["' foo '", TrimMode::UNSPECIFIED, false, 'foo'],
            ["' foo '", TrimMode::LEADING, false, 'foo '],
            ["' foo '", TrimMode::TRAILING, false, ' foo'],
            ["' foo '", TrimMode::BOTH, false, 'foo'],
            ["' foo '", TrimMode::UNSPECIFIED, "'f'", ' foo '],
            ["' foo '", TrimMode::UNSPECIFIED, "'o'", ' foo '],
            ["' foo '", TrimMode::UNSPECIFIED, "'.'", ' foo '],
            ["' foo '", TrimMode::UNSPECIFIED, "' '", 'foo'],
            ["' foo '", TrimMode::LEADING, "'f'", ' foo '],
            ["' foo '", TrimMode::LEADING, "'o'", ' foo '],
            ["' foo '", TrimMode::LEADING, "'.'", ' foo '],
            ["' foo '", TrimMode::LEADING, "' '", 'foo '],
            ["' foo '", TrimMode::TRAILING, "'f'", ' foo '],
            ["' foo '", TrimMode::TRAILING, "'o'", ' foo '],
            ["' foo '", TrimMode::TRAILING, "'.'", ' foo '],
            ["' foo '", TrimMode::TRAILING, "' '", ' foo'],
            ["' foo '", TrimMode::BOTH, "'f'", ' foo '],
            ["' foo '", TrimMode::BOTH, "'o'", ' foo '],
            ["' foo '", TrimMode::BOTH, "'.'", ' foo '],
            ["' foo '", TrimMode::BOTH, "' '", 'foo'],
Sergei Morozov's avatar
Sergei Morozov committed
565
        ];
566 567
    }

568 569 570
    /**
     * @group DDC-1014
     */
571
    public function testDateArithmetics(): void
572
    {
Sergei Morozov's avatar
Sergei Morozov committed
573
        $p    = $this->connection->getDatabasePlatform();
Sergei Morozov's avatar
Sergei Morozov committed
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
        $sql  = 'SELECT ';
        $sql .= $p->getDateAddSecondsExpression('test_datetime', 1) . ' AS add_seconds, ';
        $sql .= $p->getDateSubSecondsExpression('test_datetime', 1) . ' AS sub_seconds, ';
        $sql .= $p->getDateAddMinutesExpression('test_datetime', 5) . ' AS add_minutes, ';
        $sql .= $p->getDateSubMinutesExpression('test_datetime', 5) . ' AS sub_minutes, ';
        $sql .= $p->getDateAddHourExpression('test_datetime', 3) . ' AS add_hour, ';
        $sql .= $p->getDateSubHourExpression('test_datetime', 3) . ' AS sub_hour, ';
        $sql .= $p->getDateAddDaysExpression('test_datetime', 10) . ' AS add_days, ';
        $sql .= $p->getDateSubDaysExpression('test_datetime', 10) . ' AS sub_days, ';
        $sql .= $p->getDateAddWeeksExpression('test_datetime', 1) . ' AS add_weeks, ';
        $sql .= $p->getDateSubWeeksExpression('test_datetime', 1) . ' AS sub_weeks, ';
        $sql .= $p->getDateAddMonthExpression('test_datetime', 2) . ' AS add_month, ';
        $sql .= $p->getDateSubMonthExpression('test_datetime', 2) . ' AS sub_month, ';
        $sql .= $p->getDateAddQuartersExpression('test_datetime', 3) . ' AS add_quarters, ';
        $sql .= $p->getDateSubQuartersExpression('test_datetime', 3) . ' AS sub_quarters, ';
        $sql .= $p->getDateAddYearsExpression('test_datetime', 6) . ' AS add_years, ';
        $sql .= $p->getDateSubYearsExpression('test_datetime', 6) . ' AS sub_years ';
591 592
        $sql .= 'FROM fetch_table';

Sergei Morozov's avatar
Sergei Morozov committed
593
        $row = $this->connection->fetchAssoc($sql);
594
        $row = array_change_key_case($row, CASE_LOWER);
595

Sergei Morozov's avatar
Sergei Morozov committed
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
        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');
        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');
        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');
        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');
        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');
        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');
        self::assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), 'Adding date should end up on 2010-01-11');
        self::assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), 'Subtracting date should end up on 2009-12-22');
        self::assertEquals('2010-01-08', date('Y-m-d', strtotime($row['add_weeks'])), 'Adding week should end up on 2010-01-08');
        self::assertEquals('2009-12-25', date('Y-m-d', strtotime($row['sub_weeks'])), 'Subtracting week should end up on 2009-12-25');
        self::assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), 'Adding month should end up on 2010-03-01');
        self::assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), 'Subtracting month should end up on 2009-11-01');
        self::assertEquals('2010-10-01', date('Y-m-d', strtotime($row['add_quarters'])), 'Adding quarters should end up on 2010-04-01');
        self::assertEquals('2009-04-01', date('Y-m-d', strtotime($row['sub_quarters'])), 'Subtracting quarters should end up on 2009-10-01');
        self::assertEquals('2016-01-01', date('Y-m-d', strtotime($row['add_years'])), 'Adding years should end up on 2016-01-01');
        self::assertEquals('2004-01-01', date('Y-m-d', strtotime($row['sub_years'])), 'Subtracting years should end up on 2004-01-01');
612
    }
613

614
    public function testSqliteDateArithmeticWithDynamicInterval(): void
615
    {
Sergei Morozov's avatar
Sergei Morozov committed
616
        $platform = $this->connection->getDatabasePlatform();
617 618 619 620 621 622 623 624 625 626

        if (! $platform instanceof SqlitePlatform) {
            $this->markTestSkipped('test is for sqlite only');
        }

        $table = new Table('fetch_table_date_math');
        $table->addColumn('test_date', 'date');
        $table->addColumn('test_days', 'integer');
        $table->setPrimaryKey(['test_date']);

Sergei Morozov's avatar
Sergei Morozov committed
627
        $sm = $this->connection->getSchemaManager();
628 629
        $sm->createTable($table);

Sergei Morozov's avatar
Sergei Morozov committed
630 631
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-01-01', 'test_days' => 10]);
        $this->connection->insert('fetch_table_date_math', ['test_date' => '2010-06-01', 'test_days' => 20]);
632 633 634 635

        $sql  = 'SELECT COUNT(*) FROM fetch_table_date_math WHERE ';
        $sql .= $platform->getDateSubDaysExpression('test_date', 'test_days') . " < '2010-05-12'";

Sergei Morozov's avatar
Sergei Morozov committed
636
        $rowCount = $this->connection->fetchColumn($sql, [], 0);
637 638 639 640

        $this->assertEquals(1, $rowCount);
    }

641
    public function testLocateExpression(): void
642
    {
Sergei Morozov's avatar
Sergei Morozov committed
643
        $platform = $this->connection->getDatabasePlatform();
644

Sergei Morozov's avatar
Sergei Morozov committed
645 646 647 648 649 650 651 652 653 654
        $sql  = 'SELECT ';
        $sql .= $platform->getLocateExpression('test_string', "'oo'") . ' AS locate1, ';
        $sql .= $platform->getLocateExpression('test_string', "'foo'") . ' AS locate2, ';
        $sql .= $platform->getLocateExpression('test_string', "'bar'") . ' AS locate3, ';
        $sql .= $platform->getLocateExpression('test_string', 'test_string') . ' AS locate4, ';
        $sql .= $platform->getLocateExpression("'foo'", 'test_string') . ' AS locate5, ';
        $sql .= $platform->getLocateExpression("'barfoobaz'", 'test_string') . ' AS locate6, ';
        $sql .= $platform->getLocateExpression("'bar'", 'test_string') . ' AS locate7, ';
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 2) . ' AS locate8, ';
        $sql .= $platform->getLocateExpression('test_string', "'oo'", 3) . ' AS locate9 ';
655 656
        $sql .= 'FROM fetch_table';

Sergei Morozov's avatar
Sergei Morozov committed
657
        $row = $this->connection->fetchAssoc($sql);
658 659
        $row = array_change_key_case($row, CASE_LOWER);

660 661 662 663 664 665 666 667 668
        self::assertEquals(2, $row['locate1']);
        self::assertEquals(1, $row['locate2']);
        self::assertEquals(0, $row['locate3']);
        self::assertEquals(1, $row['locate4']);
        self::assertEquals(1, $row['locate5']);
        self::assertEquals(4, $row['locate6']);
        self::assertEquals(0, $row['locate7']);
        self::assertEquals(2, $row['locate8']);
        self::assertEquals(0, $row['locate9']);
669 670
    }

671
    public function testQuoteSQLInjection(): void
672
    {
Sergei Morozov's avatar
Sergei Morozov committed
673 674
        $sql  = 'SELECT * FROM fetch_table WHERE test_string = ' . $this->connection->quote("bar' OR '1'='1");
        $rows = $this->connection->fetchAll($sql);
675

Sergei Morozov's avatar
Sergei Morozov committed
676
        self::assertCount(0, $rows, 'no result should be returned, otherwise SQL injection is possible');
677
    }
678

Fabio B. Silva's avatar
Fabio B. Silva committed
679 680 681
    /**
     * @group DDC-1213
     */
682
    public function testBitComparisonExpressionSupport(): void
Fabio B. Silva's avatar
Fabio B. Silva committed
683
    {
Sergei Morozov's avatar
Sergei Morozov committed
684 685
        $this->connection->exec('DELETE FROM fetch_table');
        $platform = $this->connection->getDatabasePlatform();
Sergei Morozov's avatar
Sergei Morozov committed
686
        $bitmap   = [];
687

Sergei Morozov's avatar
Sergei Morozov committed
688 689
        for ($i = 2; $i < 9; $i += 2) {
            $bitmap[$i] = [
Fabio B. Silva's avatar
Fabio B. Silva committed
690
                'bit_or'    => ($i | 2),
Sergei Morozov's avatar
Sergei Morozov committed
691 692
                'bit_and'   => ($i & 2),
            ];
Sergei Morozov's avatar
Sergei Morozov committed
693
            $this->connection->insert('fetch_table', [
694
                'test_int'      => $i,
Fabio B. Silva's avatar
Fabio B. Silva committed
695
                'test_string'   => json_encode($bitmap[$i]),
Sergei Morozov's avatar
Sergei Morozov committed
696 697
                'test_datetime' => '2010-01-01 10:10:10',
            ]);
Fabio B. Silva's avatar
Fabio B. Silva committed
698
        }
699

Sergei Morozov's avatar
Sergei Morozov committed
700 701 702 703 704 705
        $sql[] = 'SELECT ';
        $sql[] = 'test_int, ';
        $sql[] = 'test_string, ';
        $sql[] = $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or, ';
        $sql[] = $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and ';
        $sql[] = 'FROM fetch_table';
Fabio B. Silva's avatar
Fabio B. Silva committed
706

Sergei Morozov's avatar
Sergei Morozov committed
707
        $stmt = $this->connection->executeQuery(implode(PHP_EOL, $sql));
708
        $data = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
709

Gabriel Caruso's avatar
Gabriel Caruso committed
710
        self::assertCount(4, $data);
711
        self::assertEquals(count($bitmap), count($data));
Fabio B. Silva's avatar
Fabio B. Silva committed
712
        foreach ($data as $row) {
713 714
            $row = array_change_key_case($row, CASE_LOWER);

715
            self::assertArrayHasKey('test_int', $row);
716

Fabio B. Silva's avatar
Fabio B. Silva committed
717
            $id = $row['test_int'];
718

719 720
            self::assertArrayHasKey($id, $bitmap);
            self::assertArrayHasKey($id, $bitmap);
721

722 723
            self::assertArrayHasKey('bit_or', $row);
            self::assertArrayHasKey('bit_and', $row);
724

725 726
            self::assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
            self::assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
Fabio B. Silva's avatar
Fabio B. Silva committed
727 728
        }
    }
729

730
    public function testSetDefaultFetchMode(): void
731
    {
Sergei Morozov's avatar
Sergei Morozov committed
732
        $stmt = $this->connection->query('SELECT * FROM fetch_table');
733
        $stmt->setFetchMode(FetchMode::NUMERIC);
734 735

        $row = array_keys($stmt->fetch());
Sergei Morozov's avatar
Sergei Morozov committed
736 737 738
        self::assertCount(0, array_filter($row, static function ($v) {
            return ! is_numeric($v);
        }), 'should be no non-numerical elements in the result.');
739
    }
Fabio B. Silva's avatar
Fabio B. Silva committed
740

741 742 743
    /**
     * @group DBAL-1091
     */
744
    public function testFetchAllStyleObject(): void
745 746 747
    {
        $this->setupFixture();

Sergei Morozov's avatar
Sergei Morozov committed
748
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
Sergei Morozov's avatar
Sergei Morozov committed
749
        $stmt = $this->connection->prepare($sql);
750 751 752

        $stmt->execute();

753
        $results = $stmt->fetchAll(FetchMode::STANDARD_OBJECT);
754

755 756
        self::assertCount(1, $results);
        self::assertInstanceOf('stdClass', $results[0]);
757

758
        self::assertEquals(
759 760 761
            1,
            property_exists($results[0], 'test_int') ? $results[0]->test_int : $results[0]->TEST_INT
        );
762
        self::assertEquals(
763 764 765
            'foo',
            property_exists($results[0], 'test_string') ? $results[0]->test_string : $results[0]->TEST_STRING
        );
766
        self::assertStringStartsWith(
767 768 769 770 771
            '2010-01-01 10:10:10',
            property_exists($results[0], 'test_datetime') ? $results[0]->test_datetime : $results[0]->TEST_DATETIME
        );
    }

Fabio B. Silva's avatar
Fabio B. Silva committed
772 773 774
    /**
     * @group DBAL-196
     */
775
    public function testFetchAllSupportFetchClass(): void
Fabio B. Silva's avatar
Fabio B. Silva committed
776
    {
777
        $this->beforeFetchClassTest();
778
        $this->setupFixture();
Fabio B. Silva's avatar
Fabio B. Silva committed
779

Sergei Morozov's avatar
Sergei Morozov committed
780
        $sql  = 'SELECT test_int, test_string, test_datetime FROM fetch_table';
Sergei Morozov's avatar
Sergei Morozov committed
781
        $stmt = $this->connection->prepare($sql);
Fabio B. Silva's avatar
Fabio B. Silva committed
782 783 784
        $stmt->execute();

        $results = $stmt->fetchAll(
785
            FetchMode::CUSTOM_OBJECT,
Sergei Morozov's avatar
Sergei Morozov committed
786
            MyFetchClass::class
Fabio B. Silva's avatar
Fabio B. Silva committed
787 788
        );

Gabriel Caruso's avatar
Gabriel Caruso committed
789
        self::assertCount(1, $results);
Sergei Morozov's avatar
Sergei Morozov committed
790
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
Benjamin Eberlei's avatar
Benjamin Eberlei committed
791

792 793 794
        self::assertEquals(1, $results[0]->test_int);
        self::assertEquals('foo', $results[0]->test_string);
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
Fabio B. Silva's avatar
Fabio B. Silva committed
795
    }
796 797 798 799

    /**
     * @group DBAL-241
     */
800
    public function testFetchAllStyleColumn(): void
801
    {
Sergei Morozov's avatar
Sergei Morozov committed
802
        $sql = 'DELETE FROM fetch_table';
Sergei Morozov's avatar
Sergei Morozov committed
803
        $this->connection->executeUpdate($sql);
804

Sergei Morozov's avatar
Sergei Morozov committed
805 806
        $this->connection->insert('fetch_table', ['test_int' => 1, 'test_string' => 'foo']);
        $this->connection->insert('fetch_table', ['test_int' => 10, 'test_string' => 'foo']);
807

Sergei Morozov's avatar
Sergei Morozov committed
808
        $sql  = 'SELECT test_int FROM fetch_table';
Sergei Morozov's avatar
Sergei Morozov committed
809
        $rows = $this->connection->query($sql)->fetchAll(FetchMode::COLUMN);
810

Sergei Morozov's avatar
Sergei Morozov committed
811
        self::assertEquals([1, 10], $rows);
812
    }
813 814 815 816

    /**
     * @group DBAL-214
     */
817
    public function testSetFetchModeClassFetchAll(): void
818
    {
819
        $this->beforeFetchClassTest();
820 821
        $this->setupFixture();

Sergei Morozov's avatar
Sergei Morozov committed
822
        $sql  = 'SELECT * FROM fetch_table';
Sergei Morozov's avatar
Sergei Morozov committed
823 824
        $stmt = $this->connection->query($sql);
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
825 826 827

        $results = $stmt->fetchAll();

Gabriel Caruso's avatar
Gabriel Caruso committed
828
        self::assertCount(1, $results);
Sergei Morozov's avatar
Sergei Morozov committed
829
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
830

831 832 833
        self::assertEquals(1, $results[0]->test_int);
        self::assertEquals('foo', $results[0]->test_string);
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
834 835 836 837 838
    }

    /**
     * @group DBAL-214
     */
839
    public function testSetFetchModeClassFetch(): void
840
    {
841
        $this->beforeFetchClassTest();
842 843
        $this->setupFixture();

Sergei Morozov's avatar
Sergei Morozov committed
844
        $sql  = 'SELECT * FROM fetch_table';
Sergei Morozov's avatar
Sergei Morozov committed
845 846
        $stmt = $this->connection->query($sql);
        $stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MyFetchClass::class);
847

Sergei Morozov's avatar
Sergei Morozov committed
848
        $results = [];
849 850 851 852
        while ($row = $stmt->fetch()) {
            $results[] = $row;
        }

Gabriel Caruso's avatar
Gabriel Caruso committed
853
        self::assertCount(1, $results);
Sergei Morozov's avatar
Sergei Morozov committed
854
        self::assertInstanceOf(MyFetchClass::class, $results[0]);
855

856 857 858
        self::assertEquals(1, $results[0]->test_int);
        self::assertEquals('foo', $results[0]->test_string);
        self::assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
859 860
    }

861 862 863
    /**
     * @group DBAL-257
     */
864
    public function testEmptyFetchColumnReturnsFalse(): void
865
    {
Sergei Morozov's avatar
Sergei Morozov committed
866 867 868 869 870
        $this->connection->beginTransaction();
        $this->connection->exec('DELETE FROM fetch_table');
        self::assertFalse($this->connection->fetchColumn('SELECT test_int FROM fetch_table'));
        self::assertFalse($this->connection->query('SELECT test_int FROM fetch_table')->fetchColumn());
        $this->connection->rollBack();
871 872
    }

873 874 875
    /**
     * @group DBAL-339
     */
876
    public function testSetFetchModeOnDbalStatement(): void
877
    {
Sergei Morozov's avatar
Sergei Morozov committed
878
        $sql  = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
Sergei Morozov's avatar
Sergei Morozov committed
879
        $stmt = $this->connection->executeQuery($sql, [1, 'foo']);
880
        $stmt->setFetchMode(FetchMode::NUMERIC);
881

Luís Cobucci's avatar
Luís Cobucci committed
882 883 884 885 886
        $row = $stmt->fetch();

        self::assertArrayHasKey(0, $row);
        self::assertArrayHasKey(1, $row);
        self::assertFalse($stmt->fetch());
887 888
    }

889 890 891
    /**
     * @group DBAL-435
     */
892
    public function testEmptyParameters(): void
893
    {
Sergei Morozov's avatar
Sergei Morozov committed
894
        $sql  = 'SELECT * FROM fetch_table WHERE test_int IN (?)';
Sergei Morozov's avatar
Sergei Morozov committed
895
        $stmt = $this->connection->executeQuery($sql, [[]], [Connection::PARAM_INT_ARRAY]);
896 897
        $rows = $stmt->fetchAll();

Sergei Morozov's avatar
Sergei Morozov committed
898
        self::assertEquals([], $rows);
899 900
    }

901 902 903
    /**
     * @group DBAL-1028
     */
904
    public function testFetchColumnNullValue(): void
905
    {
Sergei Morozov's avatar
Sergei Morozov committed
906
        $this->connection->executeUpdate(
907
            'INSERT INTO fetch_table (test_int, test_string) VALUES (?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
908
            [2, 'foo']
909 910
        );

911
        self::assertNull(
Sergei Morozov's avatar
Sergei Morozov committed
912
            $this->connection->fetchColumn('SELECT test_datetime FROM fetch_table WHERE test_int = ?', [2])
913 914 915 916 917 918
        );
    }

    /**
     * @group DBAL-1028
     */
919
    public function testFetchColumnNoResult(): void
920
    {
921
        self::assertFalse(
Sergei Morozov's avatar
Sergei Morozov committed
922
            $this->connection->fetchColumn('SELECT test_int FROM fetch_table WHERE test_int = ?', [-1])
923 924 925
        );
    }

926
    private function setupFixture(): void
927
    {
Sergei Morozov's avatar
Sergei Morozov committed
928 929
        $this->connection->exec('DELETE FROM fetch_table');
        $this->connection->insert('fetch_table', [
930 931
            'test_int'      => 1,
            'test_string'   => 'foo',
Sergei Morozov's avatar
Sergei Morozov committed
932 933
            'test_datetime' => '2010-01-01 10:10:10',
        ]);
934 935
    }

936
    private function beforeFetchClassTest(): void
937
    {
938 939 940
        $driver = $this->connection->getDriver();

        if ($driver instanceof Oci8Driver) {
Sergei Morozov's avatar
Sergei Morozov committed
941
            $this->markTestSkipped('Not supported by OCI8');
942
        }
943 944 945 946 947 948

        if ($driver instanceof MySQLiDriver) {
            $this->markTestSkipped('Mysqli driver dont support this feature.');
        }

        if (! $driver instanceof PDOOracleDriver) {
Sergei Morozov's avatar
Sergei Morozov committed
949
            return;
950
        }
Sergei Morozov's avatar
Sergei Morozov committed
951

952
        $connection = $this->connection->getWrappedConnection();
953
        self::assertInstanceOf(PDOConnection::class, $connection);
954
        $connection->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
955
    }
Fabio B. Silva's avatar
Fabio B. Silva committed
956 957 958 959
}

class MyFetchClass
{
Sergei Morozov's avatar
Sergei Morozov committed
960 961 962 963 964 965 966 967
    /** @var int */
    public $test_int;

    /** @var string */
    public $test_string;

    /** @var string */
    public $test_datetime;
968
}