ConnectionTest.php 27.9 KB
Newer Older
jwage's avatar
jwage committed
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL;

5
use Doctrine\Common\Cache\Cache;
6
use Doctrine\Common\EventManager;
Luís Cobucci's avatar
Luís Cobucci committed
7
use Doctrine\DBAL\Cache\ArrayStatement;
8
use Doctrine\DBAL\Cache\QueryCacheProfile;
9
use Doctrine\DBAL\Configuration;
jeroendedauw's avatar
jeroendedauw committed
10
use Doctrine\DBAL\Connection;
Luís Cobucci's avatar
Luís Cobucci committed
11
use Doctrine\DBAL\ConnectionException;
12
use Doctrine\DBAL\DBALException;
13
use Doctrine\DBAL\Driver;
14
use Doctrine\DBAL\Driver\Connection as DriverConnection;
15
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
Sergei Morozov's avatar
Sergei Morozov committed
16 17
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\DriverManager;
18
use Doctrine\DBAL\Events;
Luís Cobucci's avatar
Luís Cobucci committed
19
use Doctrine\DBAL\Exception\InvalidArgumentException;
20
use Doctrine\DBAL\FetchMode;
Sergei Morozov's avatar
Sergei Morozov committed
21 22
use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Logging\EchoSQLLogger;
23
use Doctrine\DBAL\ParameterType;
24
use Doctrine\DBAL\Platforms\AbstractPlatform;
25
use Doctrine\DBAL\VersionAwarePlatformDriver;
Sergei Morozov's avatar
Sergei Morozov committed
26 27
use Doctrine\Tests\DbalTestCase;
use Exception;
28
use PHPUnit\Framework\MockObject\MockObject;
Sergei Morozov's avatar
Sergei Morozov committed
29
use stdClass;
30
use function call_user_func_array;
31

32 33 34
/**
 * @requires extension pdo_mysql
 */
Sergei Morozov's avatar
Sergei Morozov committed
35
class ConnectionTest extends DbalTestCase
jwage's avatar
jwage committed
36
{
Sergei Morozov's avatar
Sergei Morozov committed
37
    /** @var Connection */
Sergei Morozov's avatar
Sergei Morozov committed
38
    private $connection;
39

Sergei Morozov's avatar
Sergei Morozov committed
40 41
    /** @var string[] */
    protected $params = [
42 43 44 45
        'driver' => 'pdo_mysql',
        'host' => 'localhost',
        'user' => 'root',
        'password' => 'password',
Sergei Morozov's avatar
Sergei Morozov committed
46 47
        'port' => '1234',
    ];
48

49
    protected function setUp() : void
jwage's avatar
jwage committed
50
    {
Sergei Morozov's avatar
Sergei Morozov committed
51
        $this->connection = DriverManager::getConnection($this->params);
jwage's avatar
jwage committed
52 53
    }

54 55 56 57
    /**
     * @return Connection|MockObject
     */
    private function getExecuteUpdateMockConnection()
58
    {
Sergei Morozov's avatar
Sergei Morozov committed
59
        $driverMock = $this->createMock(Driver::class);
60 61 62

        $driverMock->expects($this->any())
            ->method('connect')
63 64 65
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
66

67 68
        $platform = $this->getMockForAbstractClass(AbstractPlatform::class);

Sergei Morozov's avatar
Sergei Morozov committed
69
        return $this->getMockBuilder(Connection::class)
70
            ->setMethods(['executeUpdate'])
71
            ->setConstructorArgs([['platform' => $platform], $driverMock])
72 73 74
            ->getMock();
    }

75 76
    public function testIsConnected()
    {
Sergei Morozov's avatar
Sergei Morozov committed
77
        self::assertFalse($this->connection->isConnected());
78 79 80 81
    }

    public function testNoTransactionActiveByDefault()
    {
Sergei Morozov's avatar
Sergei Morozov committed
82
        self::assertFalse($this->connection->isTransactionActive());
83 84
    }

Sergei Morozov's avatar
Sergei Morozov committed
85
    public function testCommitWithNoActiveTransactionThrowsException()
86
    {
Luís Cobucci's avatar
Luís Cobucci committed
87
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
88
        $this->connection->commit();
89 90
    }

Sergei Morozov's avatar
Sergei Morozov committed
91
    public function testRollbackWithNoActiveTransactionThrowsException()
92
    {
Luís Cobucci's avatar
Luís Cobucci committed
93
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
94
        $this->connection->rollBack();
95 96
    }

Sergei Morozov's avatar
Sergei Morozov committed
97
    public function testSetRollbackOnlyNoActiveTransactionThrowsException()
98
    {
Luís Cobucci's avatar
Luís Cobucci committed
99
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
100
        $this->connection->setRollbackOnly();
101 102
    }

Sergei Morozov's avatar
Sergei Morozov committed
103
    public function testIsRollbackOnlyNoActiveTransactionThrowsException()
104
    {
Luís Cobucci's avatar
Luís Cobucci committed
105
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
106
        $this->connection->isRollbackOnly();
107 108 109 110
    }

    public function testGetConfiguration()
    {
Sergei Morozov's avatar
Sergei Morozov committed
111
        $config = $this->connection->getConfiguration();
112

Sergei Morozov's avatar
Sergei Morozov committed
113
        self::assertInstanceOf(Configuration::class, $config);
114 115
    }

jwage's avatar
jwage committed
116 117
    public function testGetHost()
    {
Sergei Morozov's avatar
Sergei Morozov committed
118
        self::assertEquals('localhost', $this->connection->getHost());
jwage's avatar
jwage committed
119 120 121 122
    }

    public function testGetPort()
    {
Sergei Morozov's avatar
Sergei Morozov committed
123
        self::assertEquals('1234', $this->connection->getPort());
jwage's avatar
jwage committed
124 125 126 127
    }

    public function testGetUsername()
    {
Sergei Morozov's avatar
Sergei Morozov committed
128
        self::assertEquals('root', $this->connection->getUsername());
jwage's avatar
jwage committed
129 130 131 132
    }

    public function testGetPassword()
    {
Sergei Morozov's avatar
Sergei Morozov committed
133
        self::assertEquals('password', $this->connection->getPassword());
jwage's avatar
jwage committed
134 135 136 137
    }

    public function testGetDriver()
    {
Sergei Morozov's avatar
Sergei Morozov committed
138
        self::assertInstanceOf(\Doctrine\DBAL\Driver\PDOMySql\Driver::class, $this->connection->getDriver());
jwage's avatar
jwage committed
139 140 141 142
    }

    public function testGetEventManager()
    {
Sergei Morozov's avatar
Sergei Morozov committed
143
        self::assertInstanceOf(EventManager::class, $this->connection->getEventManager());
144 145 146 147
    }

    public function testConnectDispatchEvent()
    {
148
        $listenerMock = $this->getMockBuilder('ConnectDispatchEventListener')
Sergei Morozov's avatar
Sergei Morozov committed
149
            ->setMethods(['postConnect'])
150
            ->getMock();
151 152 153
        $listenerMock->expects($this->once())->method('postConnect');

        $eventManager = new EventManager();
Sergei Morozov's avatar
Sergei Morozov committed
154
        $eventManager->addEventListener([Events::postConnect], $listenerMock);
155

Sergei Morozov's avatar
Sergei Morozov committed
156
        $driverMock = $this->createMock(Driver::class);
Sergei Morozov's avatar
Sergei Morozov committed
157
        $driverMock->expects($this->at(0))
158 159
                   ->method('connect');

160
        $conn = new Connection([], $driverMock, new Configuration(), $eventManager);
161
        $conn->connect();
jwage's avatar
jwage committed
162
    }
163

164 165
    public function testEventManagerPassedToPlatform()
    {
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
        $eventManager = new EventManager();

        /** @var AbstractPlatform|MockObject $driver */
        $platform = $this->createMock(AbstractPlatform::class);
        $platform->expects($this->once())
            ->method('setEventManager')
            ->with($eventManager);

        /** @var Driver|MockObject $driver */
        $driver = $this->createMock(Driver::class);
        $driver->expects($this->any())
            ->method('getDatabasePlatform')
            ->willReturn($platform);

        $connection = new Connection($this->params, $driver, null, $eventManager);
        $connection->getDatabasePlatform();
182 183
    }

184
    /**
185
     * @requires extension pdo_sqlite
186 187 188 189
     * @dataProvider getQueryMethods
     */
    public function testDriverExceptionIsWrapped($method)
    {
Luís Cobucci's avatar
Luís Cobucci committed
190 191
        $this->expectException(DBALException::class);
        $this->expectExceptionMessage("An exception occurred while executing 'MUUHAAAAHAAAA':\n\nSQLSTATE[HY000]: General error: 1 near \"MUUHAAAAHAAAA\"");
192

Sergei Morozov's avatar
Sergei Morozov committed
193
        $connection = DriverManager::getConnection([
194 195
            'driver' => 'pdo_sqlite',
            'memory' => true,
Sergei Morozov's avatar
Sergei Morozov committed
196
        ]);
197

Sergei Morozov's avatar
Sergei Morozov committed
198
        $connection->$method('MUUHAAAAHAAAA');
199 200 201 202
    }

    public function getQueryMethods()
    {
Sergei Morozov's avatar
Sergei Morozov committed
203 204 205 206 207 208 209
        return [
            ['exec'],
            ['query'],
            ['executeQuery'],
            ['executeUpdate'],
            ['prepare'],
        ];
210 211
    }

212 213 214 215 216 217 218
    /**
     * Pretty dumb test, however we want to check that the EchoSQLLogger correctly implements the interface.
     *
     * @group DBAL-11
     */
    public function testEchoSQLLogger()
    {
Sergei Morozov's avatar
Sergei Morozov committed
219
        $logger = new EchoSQLLogger();
Sergei Morozov's avatar
Sergei Morozov committed
220 221
        $this->connection->getConfiguration()->setSQLLogger($logger);
        self::assertSame($logger, $this->connection->getConfiguration()->getSQLLogger());
222 223 224 225 226 227 228 229 230
    }

    /**
     * Pretty dumb test, however we want to check that the DebugStack correctly implements the interface.
     *
     * @group DBAL-11
     */
    public function testDebugSQLStack()
    {
Sergei Morozov's avatar
Sergei Morozov committed
231
        $logger = new DebugStack();
Sergei Morozov's avatar
Sergei Morozov committed
232 233
        $this->connection->getConfiguration()->setSQLLogger($logger);
        self::assertSame($logger, $this->connection->getConfiguration()->getSQLLogger());
234
    }
235 236 237 238

    /**
     * @group DBAL-81
     */
239
    public function testIsAutoCommit()
240
    {
Sergei Morozov's avatar
Sergei Morozov committed
241
        self::assertTrue($this->connection->isAutoCommit());
242 243 244 245 246 247 248
    }

    /**
     * @group DBAL-81
     */
    public function testSetAutoCommit()
    {
Sergei Morozov's avatar
Sergei Morozov committed
249 250 251 252
        $this->connection->setAutoCommit(false);
        self::assertFalse($this->connection->isAutoCommit());
        $this->connection->setAutoCommit(0);
        self::assertFalse($this->connection->isAutoCommit());
253 254 255 256 257 258 259
    }

    /**
     * @group DBAL-81
     */
    public function testConnectStartsTransactionInNoAutoCommitMode()
    {
Sergei Morozov's avatar
Sergei Morozov committed
260
        $driverMock = $this->createMock(Driver::class);
261 262
        $driverMock->expects($this->any())
            ->method('connect')
263 264 265
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
266
        $conn = new Connection([], $driverMock);
267 268 269

        $conn->setAutoCommit(false);

270
        self::assertFalse($conn->isTransactionActive());
271 272 273

        $conn->connect();

274
        self::assertTrue($conn->isTransactionActive());
275 276 277 278 279 280 281
    }

    /**
     * @group DBAL-81
     */
    public function testCommitStartsTransactionInNoAutoCommitMode()
    {
Sergei Morozov's avatar
Sergei Morozov committed
282
        $driverMock = $this->createMock(Driver::class);
283 284
        $driverMock->expects($this->any())
            ->method('connect')
285 286 287
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
288
        $conn = new Connection([], $driverMock);
289 290 291 292 293

        $conn->setAutoCommit(false);
        $conn->connect();
        $conn->commit();

294
        self::assertTrue($conn->isTransactionActive());
295 296 297 298 299 300 301
    }

    /**
     * @group DBAL-81
     */
    public function testRollBackStartsTransactionInNoAutoCommitMode()
    {
Sergei Morozov's avatar
Sergei Morozov committed
302
        $driverMock = $this->createMock(Driver::class);
303 304
        $driverMock->expects($this->any())
            ->method('connect')
305 306 307
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
308
        $conn = new Connection([], $driverMock);
309 310 311 312 313

        $conn->setAutoCommit(false);
        $conn->connect();
        $conn->rollBack();

314
        self::assertTrue($conn->isTransactionActive());
315 316 317 318 319 320 321
    }

    /**
     * @group DBAL-81
     */
    public function testSwitchingAutoCommitModeCommitsAllCurrentTransactions()
    {
Sergei Morozov's avatar
Sergei Morozov committed
322
        $driverMock = $this->createMock(Driver::class);
323 324
        $driverMock->expects($this->any())
            ->method('connect')
325 326 327
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
328
        $conn = new Connection([], $driverMock);
329 330 331 332 333 334

        $conn->connect();
        $conn->beginTransaction();
        $conn->beginTransaction();
        $conn->setAutoCommit(false);

335
        self::assertSame(1, $conn->getTransactionNestingLevel());
336 337 338 339 340

        $conn->beginTransaction();
        $conn->beginTransaction();
        $conn->setAutoCommit(true);

341
        self::assertFalse($conn->isTransactionActive());
342
    }
343 344 345

    public function testEmptyInsert()
    {
346
        $conn = $this->getExecuteUpdateMockConnection();
347 348 349 350 351

        $conn->expects($this->once())
            ->method('executeUpdate')
            ->with('INSERT INTO footable () VALUES ()');

Sergei Morozov's avatar
Sergei Morozov committed
352
        $conn->insert('footable', []);
353
    }
354

355 356 357 358 359
    /**
     * @group DBAL-2511
     */
    public function testUpdateWithDifferentColumnsInDataAndIdentifiers()
    {
360
        $conn = $this->getExecuteUpdateMockConnection();
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

        $conn->expects($this->once())
            ->method('executeUpdate')
            ->with(
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND name = ?',
                [
                    'some text',
                    true,
                    1,
                    'foo',
                ],
                [
                    'string',
                    'boolean',
                    'integer',
                    'string',
                ]
            );

        $conn->update(
            'TestTable',
            [
                'text' => 'some text',
                'is_edited' => true,
            ],
            [
                'id' => 1,
                'name' => 'foo',
            ],
            [
                'text' => 'string',
                'is_edited' => 'boolean',
                'id' => 'integer',
                'name' => 'string',
            ]
        );
    }

    /**
     * @group DBAL-2511
     */
    public function testUpdateWithSameColumnInDataAndIdentifiers()
    {
404
        $conn = $this->getExecuteUpdateMockConnection();
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441

        $conn->expects($this->once())
            ->method('executeUpdate')
            ->with(
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND is_edited = ?',
                [
                    'some text',
                    true,
                    1,
                    false,
                ],
                [
                    'string',
                    'boolean',
                    'integer',
                    'boolean',
                ]
            );

        $conn->update(
            'TestTable',
            [
                'text' => 'some text',
                'is_edited' => true,
            ],
            [
                'id' => 1,
                'is_edited' => false,
            ],
            [
                'text' => 'string',
                'is_edited' => 'boolean',
                'id' => 'integer',
            ]
        );
    }

442 443 444 445 446
    /**
     * @group DBAL-2688
     */
    public function testUpdateWithIsNull()
    {
447
        $conn = $this->getExecuteUpdateMockConnection();
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488

        $conn->expects($this->once())
            ->method('executeUpdate')
            ->with(
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id IS NULL AND name = ?',
                [
                    'some text',
                    null,
                    'foo',
                ],
                [
                    'string',
                    'boolean',
                    'string',
                ]
            );

        $conn->update(
            'TestTable',
            [
                'text' => 'some text',
                'is_edited' => null,
            ],
            [
                'id' => null,
                'name' => 'foo',
            ],
            [
                'text' => 'string',
                'is_edited' => 'boolean',
                'id' => 'integer',
                'name' => 'string',
            ]
        );
    }

    /**
     * @group DBAL-2688
     */
    public function testDeleteWithIsNull()
    {
489
        $conn = $this->getExecuteUpdateMockConnection();
490 491 492 493 494

        $conn->expects($this->once())
            ->method('executeUpdate')
            ->with(
                'DELETE FROM TestTable WHERE id IS NULL AND name = ?',
Sergei Morozov's avatar
Sergei Morozov committed
495 496
                ['foo'],
                ['string']
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
            );

        $conn->delete(
            'TestTable',
            [
                'id' => null,
                'name' => 'foo',
            ],
            [
                'id' => 'integer',
                'name' => 'string',
            ]
        );
    }

512 513 514
    public function testFetchAssoc()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
515 516 517
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
518

Sergei Morozov's avatar
Sergei Morozov committed
519
        $driverMock = $this->createMock(Driver::class);
520 521 522

        $driverMock->expects($this->any())
            ->method('connect')
523 524 525
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
526

527
        $driverStatementMock = $this->createMock(Statement::class);
528 529 530

        $driverStatementMock->expects($this->once())
            ->method('fetch')
531
            ->with(FetchMode::ASSOCIATIVE)
532 533
            ->will($this->returnValue($result));

534
        /** @var Connection|MockObject $conn */
Sergei Morozov's avatar
Sergei Morozov committed
535
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
536
            ->setMethods(['executeQuery'])
537
            ->setConstructorArgs([[], $driverMock])
538 539 540 541 542 543 544
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

545
        self::assertSame($result, $conn->fetchAssoc($statement, $params, $types));
546 547 548 549 550
    }

    public function testFetchArray()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
551 552 553
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
554

Sergei Morozov's avatar
Sergei Morozov committed
555
        $driverMock = $this->createMock(Driver::class);
556 557 558

        $driverMock->expects($this->any())
            ->method('connect')
559 560 561
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
562

563
        $driverStatementMock = $this->createMock(Statement::class);
564 565 566

        $driverStatementMock->expects($this->once())
            ->method('fetch')
567
            ->with(FetchMode::NUMERIC)
568 569
            ->will($this->returnValue($result));

570
        /** @var Connection|MockObject $conn */
Sergei Morozov's avatar
Sergei Morozov committed
571
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
572
            ->setMethods(['executeQuery'])
573
            ->setConstructorArgs([[], $driverMock])
574 575 576 577 578 579 580
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

581
        self::assertSame($result, $conn->fetchArray($statement, $params, $types));
582 583 584 585 586
    }

    public function testFetchColumn()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
587 588
        $params    = [666];
        $types     = [ParameterType::INTEGER];
589
        $column    = 0;
Sergei Morozov's avatar
Sergei Morozov committed
590
        $result    = [];
591

Sergei Morozov's avatar
Sergei Morozov committed
592
        $driverMock = $this->createMock(Driver::class);
593 594 595

        $driverMock->expects($this->any())
            ->method('connect')
596 597 598
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
599

600
        $driverStatementMock = $this->createMock(Statement::class);
601 602 603 604 605 606

        $driverStatementMock->expects($this->once())
            ->method('fetchColumn')
            ->with($column)
            ->will($this->returnValue($result));

607
        /** @var Connection|MockObject $conn */
Sergei Morozov's avatar
Sergei Morozov committed
608
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
609
            ->setMethods(['executeQuery'])
610
            ->setConstructorArgs([[], $driverMock])
611 612 613 614 615 616 617
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

618
        self::assertSame($result, $conn->fetchColumn($statement, $params, $column, $types));
619
    }
malukenho's avatar
malukenho committed
620

621 622 623
    public function testFetchAll()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
624 625 626
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
627

Sergei Morozov's avatar
Sergei Morozov committed
628
        $driverMock = $this->createMock(Driver::class);
629 630 631

        $driverMock->expects($this->any())
            ->method('connect')
632 633 634
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
635

636
        $driverStatementMock = $this->createMock(Statement::class);
637 638 639 640 641

        $driverStatementMock->expects($this->once())
            ->method('fetchAll')
            ->will($this->returnValue($result));

642
        /** @var Connection|MockObject $conn */
Sergei Morozov's avatar
Sergei Morozov committed
643
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
644
            ->setMethods(['executeQuery'])
645
            ->setConstructorArgs([[], $driverMock])
646 647 648 649 650 651 652
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

653
        self::assertSame($result, $conn->fetchAll($statement, $params, $types));
654
    }
655 656 657

    public function testConnectionDoesNotMaintainTwoReferencesToExternalPDO()
    {
Sergei Morozov's avatar
Sergei Morozov committed
658
        $params['pdo'] = new stdClass();
659

Sergei Morozov's avatar
Sergei Morozov committed
660
        $driverMock = $this->createMock(Driver::class);
661 662 663

        $conn = new Connection($params, $driverMock);

Sergei Morozov's avatar
Sergei Morozov committed
664
        self::assertArrayNotHasKey('pdo', $conn->getParams(), 'Connection is maintaining additional reference to the PDO connection');
665 666 667 668
    }

    public function testPassingExternalPDOMeansConnectionIsConnected()
    {
Sergei Morozov's avatar
Sergei Morozov committed
669
        $params['pdo'] = new stdClass();
670

Sergei Morozov's avatar
Sergei Morozov committed
671
        $driverMock = $this->createMock(Driver::class);
672 673 674

        $conn = new Connection($params, $driverMock);

Sergei Morozov's avatar
Sergei Morozov committed
675
        self::assertTrue($conn->isConnected(), 'Connection is not connected after passing external PDO');
676
    }
677

678
    public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException()
679
    {
Sergei Morozov's avatar
Sergei Morozov committed
680
        /** @var Driver $driver */
Sergei Morozov's avatar
Sergei Morozov committed
681 682
        $driver  = $this->createMock(Driver::class);
        $pdoMock = $this->createMock(\Doctrine\DBAL\Driver\Connection::class);
683

684 685 686
        // should never execute queries with invalid arguments
        $pdoMock->expects($this->never())->method('exec');
        $pdoMock->expects($this->never())->method('prepare');
687

Sergei Morozov's avatar
Sergei Morozov committed
688
        $conn = new Connection(['pdo' => $pdoMock], $driver);
689

Luís Cobucci's avatar
Luís Cobucci committed
690
        $this->expectException(InvalidArgumentException::class);
Sergei Morozov's avatar
Sergei Morozov committed
691
        $conn->delete('kittens', []);
692
    }
693 694 695

    public function dataCallConnectOnce()
    {
Sergei Morozov's avatar
Sergei Morozov committed
696 697 698 699 700 701 702
        return [
            ['delete', ['tbl', ['id' => 12345]]],
            ['insert', ['tbl', ['data' => 'foo']]],
            ['update', ['tbl', ['data' => 'bar'], ['id' => 12345]]],
            ['prepare', ['select * from dual']],
            ['executeUpdate', ['insert into tbl (id) values (?)'], [123]],
        ];
703 704 705 706 707 708 709
    }

    /**
     * @dataProvider dataCallConnectOnce
     */
    public function testCallConnectOnce($method, $params)
    {
Sergei Morozov's avatar
Sergei Morozov committed
710
        $driverMock   = $this->createMock(Driver::class);
711 712
        $pdoMock      = $this->createMock(Connection::class);
        $platformMock = $this->createMock(AbstractPlatform::class);
Sergei Morozov's avatar
Sergei Morozov committed
713
        $stmtMock     = $this->createMock(Statement::class);
714 715 716 717 718

        $pdoMock->expects($this->any())
            ->method('prepare')
            ->will($this->returnValue($stmtMock));

Sergei Morozov's avatar
Sergei Morozov committed
719
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
720 721
            ->setConstructorArgs([['pdo' => $pdoMock, 'platform' => $platformMock], $driverMock])
            ->setMethods(['connect'])
722 723 724 725
            ->getMock();

        $conn->expects($this->once())->method('connect');

Sergei Morozov's avatar
Sergei Morozov committed
726
        call_user_func_array([$conn, $method], $params);
727
    }
728 729 730 731 732 733

    /**
     * @group DBAL-1127
     */
    public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform()
    {
734 735
        /** @var Driver|VersionAwarePlatformDriver|MockObject $driverMock */
        $driverMock = $this->createMock([Driver::class, VersionAwarePlatformDriver::class]);
736

737 738
        /** @var ServerInfoAwareConnection|MockObject $driverConnectionMock */
        $driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class);
739

740
        /** @var AbstractPlatform|MockObject $platformMock */
Sergei Morozov's avatar
Sergei Morozov committed
741
        $platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
742

Sergei Morozov's avatar
Sergei Morozov committed
743
        $connection = new Connection([], $driverMock);
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761

        $driverMock->expects($this->once())
            ->method('connect')
            ->will($this->returnValue($driverConnectionMock));

        $driverConnectionMock->expects($this->once())
            ->method('requiresQueryForServerVersion')
            ->will($this->returnValue(false));

        $driverConnectionMock->expects($this->once())
            ->method('getServerVersion')
            ->will($this->returnValue('6.6.6'));

        $driverMock->expects($this->once())
            ->method('createDatabasePlatformForVersion')
            ->with('6.6.6')
            ->will($this->returnValue($platformMock));

762
        self::assertSame($platformMock, $connection->getDatabasePlatform());
763
    }
764 765 766

    public function testConnectionParamsArePassedToTheQueryCacheProfileInExecuteCacheQuery()
    {
767
        $resultCacheDriverMock = $this->createMock(Cache::class);
768

769 770
        $resultCacheDriverMock
            ->expects($this->atLeastOnce())
771
            ->method('fetch')
Antonio Vilar's avatar
Antonio Vilar committed
772
            ->with('cacheKey')
773
            ->will($this->returnValue(['realKey' => []]));
774

775 776
        $query  = 'SELECT * FROM foo WHERE bar = ?';
        $params = [666];
777
        $types  = [ParameterType::INTEGER];
778

779
        /** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
780
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
781

782 783
        $queryCacheProfileMock
            ->expects($this->any())
784 785 786 787
            ->method('getResultCacheDriver')
            ->will($this->returnValue($resultCacheDriverMock));

        // This is our main expectation
788 789
        $queryCacheProfileMock
            ->expects($this->once())
790 791
            ->method('generateCacheKeys')
            ->with($query, $params, $types, $this->params)
792
            ->will($this->returnValue(['cacheKey', 'realKey']));
793

Sergei Morozov's avatar
Sergei Morozov committed
794
        /** @var Driver $driver */
795 796
        $driver = $this->createMock(Driver::class);

797
        self::assertInstanceOf(
798 799
            ArrayStatement::class,
            (new Connection($this->params, $driver))->executeCacheQuery($query, $params, $types, $queryCacheProfileMock)
800 801 802
        );
    }

803
    /**
804
     * @group #2821
805
     */
Sergei Morozov's avatar
Sergei Morozov committed
806
    public function testShouldNotPassPlatformInParamsToTheQueryCacheProfileInExecuteCacheQuery() : void
807 808 809 810 811 812 813 814 815
    {
        $resultCacheDriverMock = $this->createMock(Cache::class);

        $resultCacheDriverMock
            ->expects($this->atLeastOnce())
            ->method('fetch')
            ->with('cacheKey')
            ->will($this->returnValue(['realKey' => []]));

816
        /** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
817 818 819 820 821 822 823
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);

        $queryCacheProfileMock
            ->expects($this->any())
            ->method('getResultCacheDriver')
            ->will($this->returnValue($resultCacheDriverMock));

Sergei Morozov's avatar
Sergei Morozov committed
824
        $query = 'SELECT 1';
825

826
        $connectionParams = $this->params;
827 828 829 830

        $queryCacheProfileMock
            ->expects($this->once())
            ->method('generateCacheKeys')
831
            ->with($query, [], [], $connectionParams)
832 833
            ->will($this->returnValue(['cacheKey', 'realKey']));

834 835
        $connectionParams['platform'] = $this->createMock(AbstractPlatform::class);

Sergei Morozov's avatar
Sergei Morozov committed
836
        /** @var Driver $driver */
837 838
        $driver = $this->createMock(Driver::class);

839
        (new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock);
840
    }
841

842
    /**
843
     * @group #2821
844
     */
Sergei Morozov's avatar
Sergei Morozov committed
845
    public function testThrowsExceptionWhenInValidPlatformSpecified() : void
846
    {
Sergei Morozov's avatar
Sergei Morozov committed
847 848
        $connectionParams             = $this->params;
        $connectionParams['platform'] = new stdClass();
849

Sergei Morozov's avatar
Sergei Morozov committed
850
        /** @var Driver $driver */
851
        $driver = $this->createMock(Driver::class);
852 853

        $this->expectException(DBALException::class);
854 855 856 857

        new Connection($connectionParams, $driver);
    }

858 859 860 861 862
    /**
     * @group DBAL-990
     */
    public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase()
    {
863 864
        /** @var Driver|VersionAwarePlatformDriver|MockObject $driverMock */
        $driverMock = $this->createMock([Driver::class, VersionAwarePlatformDriver::class]);
865

Sergei Morozov's avatar
Sergei Morozov committed
866 867 868
        $connection        = new Connection(['dbname' => 'foo'], $driverMock);
        $originalException = new Exception('Original exception');
        $fallbackException = new Exception('Fallback exception');
869 870 871 872 873 874 875 876 877 878 879 880 881

        $driverMock->expects($this->at(0))
            ->method('connect')
            ->willThrowException($originalException);

        $driverMock->expects($this->at(1))
            ->method('connect')
            ->willThrowException($fallbackException);

        $this->expectExceptionMessage($originalException->getMessage());

        $connection->getDatabasePlatform();
    }
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932

    /**
     * @group #3194
     */
    public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGeneratingCacheKeys() : void
    {
        /** @var Driver|MockObject $driver */
        $driver = $this->createMock(Driver::class);

        /** @var AbstractPlatform|MockObject $platform */
        $platform = $this->createMock(AbstractPlatform::class);

        /** @var QueryCacheProfile|MockObject $queryCacheProfile */
        $queryCacheProfile = $this->createMock(QueryCacheProfile::class);

        /** @var Cache|MockObject $resultCacheDriver */
        $resultCacheDriver = $this->createMock(Cache::class);

        $queryCacheProfile
            ->expects($this->any())
            ->method('getResultCacheDriver')
            ->will($this->returnValue($resultCacheDriver));

        $resultCacheDriver
            ->expects($this->atLeastOnce())
            ->method('fetch')
            ->with('cacheKey')
            ->will($this->returnValue(['realKey' => []]));

        $query = 'SELECT 1';

        $params = [
            'dbname' => 'foo',
            'platform' => $platform,
        ];

        $paramsWithoutPlatform = $params;
        unset($paramsWithoutPlatform['platform']);

        $queryCacheProfile
            ->expects($this->once())
            ->method('generateCacheKeys')
            ->with($query, [], [], $paramsWithoutPlatform)
            ->will($this->returnValue(['cacheKey', 'realKey']));

        $connection = new Connection($params, $driver);

        self::assertSame($params, $connection->getParams());

        $connection->executeCacheQuery($query, [], [], $queryCacheProfile);
    }
933
}